diff --git a/DEPS b/DEPS
index 6a13a24f3..e03d693 100644
--- a/DEPS
+++ b/DEPS
@@ -304,7 +304,7 @@
   # 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': '342050dfbbbfe808eb799cd2fe817987cf111f57',
+  'src_internal_revision': '112b68415b55a35a2381caeb859f598c52056c60',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
@@ -383,7 +383,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling chromium_variations
   # and whatever else without interference from each other.
-  'chromium_variations_revision': 'a24371073c455dfc595f7113e776eb8c0234d438',
+  'chromium_variations_revision': '9e9b01285b3b1febec28c95040e7ed89e3b86bc8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -399,7 +399,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': 'cf44aab1d7aea14be5623377a80218842e3c5b17',
+  'devtools_frontend_revision': 'bd996cb88ce5278a2a7ebfb999792c0ad8eda3ae',
   # 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.
@@ -1004,7 +1004,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'deb0fefc7e682a14b37e45913a6c8ac81ab8d029',
+    '6bd51962cb20205c2a1e8fa0ed704360034d6cf3',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1460,13 +1460,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6708d95ec6d819098093cd57c746636a5bb9a4ea',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd32e1cb5717853a1837347884abc85149813c398',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'dc817257b8d98a65a5cdafd557b1ee955c19de20',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'd680c3ebbbb765c508de563623aa67943bed945c',
     'condition': 'checkout_src_internal',
   },
 
@@ -2296,7 +2296,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'vRmN7TqpQKpze5a9InwYnw47cdroddfP0fTnQMdPSmgC',
+        'version': '-7bdgWgkjSubVViGjcoekOV889jc5JD-_zwh4isjpXIC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2307,7 +2307,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'Unbf-0_tAaQqzS2efPsb5kW0_MiAIuWMDn1vHYcdUaUC',
+        'version': 'kKc697kATRyvYNkrIemhsZB-hPhYXqiliFMKPeBuYqkC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2340,7 +2340,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'xV5T7yU0_EPt5Xa8lrmItPqVNudpXLib1H4Wr8mnzQkC',
+        'version': 'yqPLXABgkAyDFYcw3nwtcN36GJdENVZmRaKpuFw9xrAC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4410,7 +4410,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '393ae9df29e5989726851fbd9316cad37dfea065',
+        '0e513f182a3f4b9b77cde29641329b6f79c604a4',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwPrerenderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwPrerenderTest.java
index 95bb389..817954f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwPrerenderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwPrerenderTest.java
@@ -69,6 +69,8 @@
 
     private static final String INITIAL_URL = "/android_webview/test/data/hello_world.html";
     private static final String PRERENDER_URL = "/android_webview/test/data/prerender.html";
+    private static final String PRERENDER_SETUP_SCRIPT_URL =
+            "/android_webview/test/data/prerender-test-setup.js";
     private AwTestContainerView mTestContainerView;
     private AwContents mAwContents;
     private AwEmbeddedTestServer mTestServer;
@@ -363,6 +365,40 @@
         histogramWatcher.pollInstrumentationThreadUntilSatisfied();
     }
 
+    // Tests speculation rules prerendering with No-Vary-Search header with multiple params.
+    @Test
+    @LargeTest
+    @Feature({"AndroidWebView"})
+    @Features.EnableFeatures({BlinkFeatures.PRERENDER2_NO_VARY_SEARCH})
+    @Features.DisableFeatures({BlinkFeatures.PRERENDER2_MEMORY_CONTROLS})
+    public void testNoVarySearchHeaderMultipleParams() throws Throwable {
+        setPreloadingAllowed(PreloadingAllowedFlags.PRERENDER_ENABLED);
+        loadInitialPage();
+
+        var histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(
+                                "Prerender.Experimental.PrerenderHostFinalStatus.SpeculationRule",
+                                /*kActivated*/ 0)
+                        .build();
+
+        final String path =
+                "/android_webview/test/data/prerender-no-vary-search-multiple-params.html";
+
+        // Start prerendering `?a=1&b=2&c=3`. This response will have
+        // `No-Vary-Search: key-order, params, except=("a" "c")` header.
+        final String prerenderingUrl = mTestServer.getURL(path.concat("?a=1&b=2&c=3"));
+        injectSpeculationRulesAndWait(prerenderingUrl);
+
+        // Navigate to `?c=3&b=20&a=1`. This doesn't exactly match the prerendering URL but should
+        // activate the prerendered page for the No-Vary-Search header.
+        final String navigatingUrl = mTestServer.getURL(path.concat("?c=3&b=20&a=1"));
+        activatePage(navigatingUrl, ActivationBy.JAVASCRIPT);
+
+        // Wait until the navigation activates the prerendered page.
+        histogramWatcher.pollInstrumentationThreadUntilSatisfied();
+    }
+
     // Tests speculation rules prerendering with No-Vary-Search header. This is similar to the
     // previous test but navigates to a URL whose search param is different from the No-Vary-Search
     // header. This should not activate the prerendered page.
@@ -409,6 +445,7 @@
         String url1 = mTestServer.getURL(INITIAL_URL.concat("?q=1"));
         String url2 = mTestServer.getURL(PRERENDER_URL);
         String url3 = mTestServer.getURL(INITIAL_URL.concat("?q=3"));
+        String scriptUrl = mTestServer.getURL(PRERENDER_SETUP_SCRIPT_URL);
 
         final TestAwContentsClient.ShouldInterceptRequestHelper helper =
                 mContentsClient.getShouldInterceptRequestHelper();
@@ -426,6 +463,11 @@
         helper.waitForCallback(callCount);
         Assert.assertEquals(helper.getUrls(), Arrays.asList(url2));
 
+        helper.clearUrls();
+        callCount = helper.getCallCount();
+        helper.waitForCallback(callCount);
+        Assert.assertEquals(helper.getUrls(), Arrays.asList(scriptUrl));
+
         callCount = helper.getCallCount();
         // Prerender activation will trigger a FrameTree swap and a RenderFrameHostChanged call.
         activatePage(url2, ActivationBy.JAVASCRIPT);
@@ -550,25 +592,38 @@
 
         // This test will attempt to prerender a non-existent URL. Generally this should fail, but
         // in this test shouldInterceptRequestHelper will serve a custom response instead.
-        final String nonExistentUrl = mTestServer.getURL("/non_existent.html");
+        final String nonExistentUrl =
+                mTestServer.getURL("/android_webview/test/data/non_existent.html");
 
         // Construct a custom response.
         FileInputStream body = new FileInputStream(UrlUtils.getIsolatedTestFilePath(PRERENDER_URL));
         WebResourceResponseInfo response = new WebResourceResponseInfo("text/html", "utf-8", body);
         shouldInterceptRequestHelper.setReturnValueForUrl(nonExistentUrl, response);
 
+        final String scriptUrl = mTestServer.getURL(PRERENDER_SETUP_SCRIPT_URL);
+        FileInputStream scriptBody =
+                new FileInputStream(UrlUtils.getIsolatedTestFilePath(PRERENDER_SETUP_SCRIPT_URL));
+        WebResourceResponseInfo scriptResponse =
+                new WebResourceResponseInfo("text/javascript", "utf-8", scriptBody);
+        shouldInterceptRequestHelper.setReturnValueForUrl(scriptUrl, scriptResponse);
+
         int currentShouldInterceptRequestCallCount = shouldInterceptRequestHelper.getCallCount();
 
         // This doesn't wait for prerendering navigation as the waiting logic is implemented on top
         // of onLoadResource that is never called when a custom response is served.
         injectSpeculationRules(nonExistentUrl);
 
-        // Ensure that ShouldInterceptRequest is called.
+        // Ensure that ShouldInterceptRequest is called for the main resource and the setup script.
         shouldInterceptRequestHelper.waitForCallback(currentShouldInterceptRequestCallCount);
         AwContentsClient.AwWebResourceRequest request =
                 shouldInterceptRequestHelper.getRequestsForUrl(nonExistentUrl);
         Assert.assertNotNull(request);
 
+        shouldInterceptRequestHelper.waitForNext();
+        AwContentsClient.AwWebResourceRequest scriptRequest =
+                shouldInterceptRequestHelper.getRequestsForUrl(scriptUrl);
+        Assert.assertNotNull(scriptRequest);
+
         // Activation with the non-existent URL should succeed.
         activatePage(nonExistentUrl, ActivationBy.JAVASCRIPT);
     }
@@ -683,6 +738,7 @@
                 mTestServer.getURL(
                         "/android_webview/test/data/prerender.html?iframeSrc="
                                 .concat(subframeUrl1));
+        String scriptUrl = mTestServer.getURL(PRERENDER_SETUP_SCRIPT_URL);
 
         final TestAwContentsClient.ShouldInterceptRequestHelper helper =
                 mContentsClient.getShouldInterceptRequestHelper();
@@ -701,6 +757,16 @@
             helper.clearUrls();
             int callCount = helper.getCallCount();
             helper.waitForCallback(callCount);
+            Assert.assertEquals(helper.getUrls(), Arrays.asList(scriptUrl));
+            AwContentsClient.AwWebResourceRequest request = helper.getRequestsForUrl(scriptUrl);
+            // Subframe navigation of prerendered page also has a Sec-Purpose header.
+            Assert.assertEquals(request.requestHeaders.get("Sec-Purpose"), "prefetch;prerender");
+        }
+
+        {
+            helper.clearUrls();
+            int callCount = helper.getCallCount();
+            helper.waitForCallback(callCount);
             Assert.assertEquals(helper.getUrls(), Arrays.asList(subframeUrl1));
             AwContentsClient.AwWebResourceRequest request = helper.getRequestsForUrl(subframeUrl1);
             // Subframe navigation of prerendered page also has a Sec-Purpose header.
diff --git a/android_webview/test/data/prerender-no-vary-search-multiple-params.html b/android_webview/test/data/prerender-no-vary-search-multiple-params.html
new file mode 100644
index 0000000..b0346f5
--- /dev/null
+++ b/android_webview/test/data/prerender-no-vary-search-multiple-params.html
@@ -0,0 +1,5 @@
+<html>
+  <body>
+  </body>
+  <script src="prerender-test-setup.js"></script>
+</html>
diff --git a/android_webview/test/data/prerender-no-vary-search-multiple-params.html.mock-http-headers b/android_webview/test/data/prerender-no-vary-search-multiple-params.html.mock-http-headers
new file mode 100644
index 0000000..71c301a
--- /dev/null
+++ b/android_webview/test/data/prerender-no-vary-search-multiple-params.html.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+No-Vary-Search: key-order, params, except=("a" "c")
diff --git a/android_webview/test/data/prerender-test-setup.js b/android_webview/test/data/prerender-test-setup.js
new file mode 100644
index 0000000..ee01fd2
--- /dev/null
+++ b/android_webview/test/data/prerender-test-setup.js
@@ -0,0 +1,27 @@
+// 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.
+
+// Objects starting with the "aw" prefix are injected by AwPrerenderTest.
+
+function createIframe(url) {
+  const iframe = document.createElement('iframe');
+  iframe.setAttribute('src', url);
+  document.body.appendChild(iframe);
+}
+
+function wasActivated() {
+  return self.performance?.getEntriesByType?.('navigation')[0]
+             ?.activationStart > 0;
+}
+
+const wasPrerendered = document.prerendering || wasActivated();
+
+if (wasActivated()) {
+  awActivationFuture.activated();
+} else {
+  document.onprerenderingchange = () => awActivationFuture.activated();
+}
+
+// Notify the primary page that this prerendered page started.
+window.localStorage.setItem('pageStarted', location.href);
diff --git a/android_webview/test/data/prerender.html b/android_webview/test/data/prerender.html
index f7c7bb6..91ca18b 100644
--- a/android_webview/test/data/prerender.html
+++ b/android_webview/test/data/prerender.html
@@ -1,36 +1,16 @@
 <html>
   <body>
   </body>
+  <script src="prerender-test-setup.js"></script>
   <script>
     // Objects starting with the "aw" prefix are injected by AwPrerenderTest.
 
-    function createIframe(url) {
-      const iframe = document.createElement("iframe");
-      iframe.setAttribute("src", url);
-      document.body.appendChild(iframe);
-    }
-
     const params = new URLSearchParams(document.location.search);
     const iframeSrc = params.get("iframeSrc");
     if (iframeSrc !== null) {
       createIframe(iframeSrc);
     }
 
-    function wasActivated() {
-      return self.performance?.getEntriesByType?.('navigation')[0]?.activationStart > 0;
-    }
-
-    const wasPrerendered = document.prerendering || wasActivated();
-
-    if (wasActivated()) {
-      awActivationFuture.activated();
-    } else {
-      document.onprerenderingchange = () => awActivationFuture.activated();
-    }
-
-    // Notify the primary page that this prerendered page started.
-    window.localStorage.setItem("pageStarted", location.href);
-
     // Post a message to Java (AwPrerenderTest.java). This should be deferred
     // until activation.
     awDeferredMessagePort.postMessage(
diff --git a/chrome/VERSION b/chrome/VERSION
index 9f278d0..ec9c8d4 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=127
 MINOR=0
-BUILD=6489
+BUILD=6491
 PATCH=0
diff --git a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_lo.xtb b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_lo.xtb
index c9a8fc3..a71efb2 100644
--- a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_lo.xtb
+++ b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_lo.xtb
@@ -39,6 +39,7 @@
 <translation id="2928243913527329289">Google | ກ່ຽວກັບຮ້ານຄ້ານີ້</translation>
 <translation id="2977480621796371840">ລຶບອອກຈາກກຸ່ມ</translation>
 <translation id="3008554597174452548">ຢຸດການຕິດຕາມສິນຄ້າ</translation>
+<translation id="3026391124054406831">ສ້າງກຸ່ມເພື່ອຈັດລະບຽບແຖບຂອງທ່ານ.</translation>
 <translation id="3052964831964880138"><ph name="PRODUCT_NAME" /> ຫຼຸດລາຄາ <ph name="PRICE_DROP" /></translation>
 <translation id="3078350928901129212">ໄອຄອນສີກຸ່ມແຖບ <ph name="COLOR_NAME" /></translation>
 <translation id="3194464645767632461">ການຈັດອັນດັບ</translation>
diff --git a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ml.xtb b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ml.xtb
index 73c30b6..8a06834 100644
--- a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ml.xtb
+++ b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_ml.xtb
@@ -39,6 +39,7 @@
 <translation id="2928243913527329289">Google | ഈ സ്റ്റോറിനെക്കുറിച്ച്</translation>
 <translation id="2977480621796371840">ഗ്രൂപ്പിൽ നിന്ന് നീക്കം ചെയ്യുക</translation>
 <translation id="3008554597174452548">ഉൽപ്പന്നം ട്രാക്ക് ചെയ്യുന്നത് നിർത്തുക</translation>
+<translation id="3026391124054406831">നിങ്ങളുടെ ടാബുകൾ ഓർഗനൈസ് ചെയ്യാൻ ഗ്രൂപ്പുകൾ സൃഷ്ടിക്കുക.</translation>
 <translation id="3052964831964880138"><ph name="PRODUCT_NAME" /> എന്നതിന് <ph name="PRICE_DROP" /> വിലക്കുറവ്</translation>
 <translation id="3078350928901129212">ടാബ് ഗ്രൂപ്പിന്റെ, <ph name="COLOR_NAME" /> നിറത്തിലുള്ള ഐക്കൺ</translation>
 <translation id="3194464645767632461">റേറ്റിംഗ്</translation>
diff --git a/chrome/app/resources/chromium_strings_lo.xtb b/chrome/app/resources/chromium_strings_lo.xtb
index 27296a15..e44b6311 100644
--- a/chrome/app/resources/chromium_strings_lo.xtb
+++ b/chrome/app/resources/chromium_strings_lo.xtb
@@ -71,6 +71,8 @@
 <translation id="2174917724755363426">ການຕິດຕັ້ງບໍ່ສຳເລັດ. ທ່ານແນ່ໃຈບໍ່ວ່າທ່ານຕ້ອງການຍົກເລີກ?</translation>
 <translation id="2185166961232948079">Chromium - ການເຂົ້າສູ່ລະບົບເຄືອຂ່າຍ - <ph name="PAGE_TITLE" /></translation>
 <translation id="2190166659037789668">ກວດສອບການອັບເດດຜິດພາດ: <ph name="UPDATE_CHECK_ERROR" />.</translation>
+<translation id="2210682093923538346">ເວັບໄຊທີ່ເປັນອັນຕະລາຍ. Chromium ໄດ້ລຶບສິດເຂົ້າເຖິງການແຈ້ງເຕືອນອອກແລ້ວ.</translation>
+<translation id="2240214816234246077">ທ່ານບໍ່ໄດ້ເຂົ້າເບິ່ງເມື່ອບໍ່ດົນມານີ້. Chromium ໄດ້ລຶບສິດເຂົ້າເຖິງ <ph name="PERMISSION_1" /> ແລະ <ph name="PERMISSION_2" /> ອອກແລ້ວ</translation>
 <translation id="2241627712206172106">ຖ້າທ່ານແຊຣ໌ຄອມພິວເຕີ, ໝູ່​ເພື່ອນ ແລະຄອບຄົວຂອງທ່ານ ສາມາດທ່ອງເນັດໄດ້ແຍກຕ່າງຫາກ ແລະຕັ້ງ Chromium ໄດ້ຕາມ​ແບບທີ່ພວກເຂົາຕ້ອງການ.</translation>
 <translation id="2313870531055795960">ກວດສອບ URL ກັບລາຍຊື່ເວັບໄຊທີ່ບໍ່ປອດໄພທີ່ຈັດເກັບໄວ້ໃນ Chromium. ຖ້າມີເວັບໄຊໃດໜຶ່ງພະຍາຍາມລັກເອົາລະຫັດຜ່ານຂອງທ່ານ ຫຼື ເມື່ອທ່ານດາວໂຫຼດໄຟລ໌ອັນຕະລາຍ, Chromium ອາດສົ່ງ URL, ຮວມທັງເນື້ອຫາບາງສ່ວນຂອງໜ້າໄປໃຫ້ Safe Browsing.</translation>
 <translation id="2329088755516916767">ເມື່ອເປີດ, Chromium ຈະໂຫຼດໜ້າເວັບກ່ອນລ່ວງໜ້າເຊິ່ງເຮັດໃຫ້ທ່ອງເວັບ ແລະ ຊອກຫາໄດ້ໄວຂຶ້ນ.</translation>
@@ -269,6 +271,7 @@
 <translation id="5690427481109656848">Google LLC</translation>
 <translation id="5698481217667032250">ສະ​ແດງ Chromium ເປັນພາສານີ້</translation>
 <translation id="569897634095159764">ບໍ່ສາມາດເຊື່ອມຕໍ່ກັບອິນເຕີເນັດໄດ້. ພຣັອກຊີເຊີບເວີຕ້ອງມີການພິສູດຢືນຢັນ.</translation>
+<translation id="5761096224651992291">ທ່ານບໍ່ໄດ້ເຂົ້າເບິ່ງເມື່ອບໍ່ດົນມານີ້. Chromium ໄດ້ລຶບສິດເຂົ້າເຖິງ <ph name="PERMISSION_1" />, <ph name="PERMISSION_2" /> ແລະ <ph name="PERMISSION_3" /> ອອກແລ້ວ</translation>
 <translation id="5800158606660203929">ປັບແຕ່ງ ແລະ ຄວບຄຸມ Chromium. ຕັ້ງ Chromium ເປັນຄ່າເລີ່ມຕົ້ນຂອງທ່ານ.</translation>
 <translation id="5809516625706423866">ບໍ່ສາມາດເຊື່ອມຕໍ່ກັບອິນເຕີເນັດໄດ້. HTTP 401 ບໍ່ໄດ້ຮັບອະນຸຍາດ. ກະລຸນາກວດສອບການຕັ້ງຄ່າພຣັອກຊີຂອງທ່ານ.</translation>
 <translation id="5862307444128926510">ຍິນ​ດີ​ຕ້ອນ​ຮັບສູ່ Chromium</translation>
@@ -354,6 +357,7 @@
 <translation id="6729124504294600478">ເພື່ອຮັບການປັບແຕ່ງເປັນແບບສ່ວນຕົວ ແລະ ຄຸນສົມບັດອື່ນໆ, ໃຫ້ຮວມ Chromium ໄວ້ໃນການເຄື່ອນໄຫວເວັບ ແລະ ແອັບ ພ້ອມທັງບໍລິການ Google ທີ່ລິ້ງໄວ້</translation>
 <translation id="6734291798041940871">ຕິດຕັ້ງ Chromium ສຳລັບຜູ້ໃຊ້ທຸກຄົນໃນຄອມພິວເຕີຂອງທ່ານແລ້ວ.</translation>
 <translation id="673636774878526923">ເພື່ອເຂົ້າເຖິງຂໍ້ມູນໃນ Chromium ຂອງທ່ານຢູ່ໃນອຸປະກອນທັງໝົດຂອງທ່ານ, ກະລຸນາເຂົ້າສູ່ລະບົບ ແລ້ວເປີດການຊິ້ງຂໍ້ມູນ.</translation>
+<translation id="674245979920622322">ທ່ານບໍ່ໄດ້ເຂົ້າເບິ່ງເມື່ອບໍ່ດົນມານີ້. Chromium ໄດ້ລຶບສິດເຂົ້າເຖິງ <ph name="PERMISSION_1" />, <ph name="PERMISSION_2" /> ແລະ ອີກ <ph name="COUNT" /> ລາຍການອອກແລ້ວ</translation>
 <translation id="67706546131546258">Chromium ແນະນຳໃຫ້ສະແກນໄຟລ໌ນີ້ເນື່ອງຈາກມັນອາດເປັນອັນຕະລາຍໄດ້.</translation>
 <translation id="6779406956731413166">ChromiumOS ເກີດຂຶ້ນໄດ້ດ້ວຍ <ph name="BEGIN_LINK_CROS_OSS" />ຊອບແວໂອເພນຊອດ<ph name="END_LINK_CROS_OSS" /> ເພີ່ມເຕີມ.</translation>
 <translation id="6847869444787758381">Chromium ແຈ້ງໃຫ້ທ່ານຮູ້ຖ້າມີຄົນຮູ້ລະຫັດຜ່ານຂອງທ່ານ</translation>
@@ -406,6 +410,7 @@
 <translation id="753534427205733210">{0,plural, =1{Chromium ຈະເປີດຄືນໃໝ່ໃນ 1 ນາທີ}other{Chromium ຈະເປີດຄືນໃໝ່ໃນ # ນາທີ}}</translation>
 <translation id="7582945390259497898">Chromium ສາມາດປະເມີນຄວາມສົນໃຈຂອງທ່ານໄດ້. ເວັບໄຊໃດໜຶ່ງທີ່ທ່ານເຂົ້າເບິ່ງຈະສາມາດຂໍ Chromium ເພື່ອເບິ່ງຄວາມສົນໃຈຂອງທ່ານໃນພາຍຫຼັງໄດ້ເພື່ອປັບແຕ່ງໂຄສະນາທີ່ທ່ານເຫັນໃຫ້ເປັນສ່ວນຕົວ.</translation>
 <translation id="7583399374488819119">ຕົວຕິດຕັ້ງ <ph name="COMPANY_NAME" /></translation>
+<translation id="7585391435984513350">ທ່ານບໍ່ໄດ້ເຂົ້າເບິ່ງເມື່ອບໍ່ດົນມານີ້. Chromium ໄດ້ລຶບສິດເຂົ້າເຖິງ <ph name="PERMISSION" /> ອອກແລ້ວ</translation>
 <translation id="761356813943268536">Chromium ກໍາລັງໃຊ້​ກ້ອງ​ຖ່າຍ​ຮູບ​ ແລະໄມໂຄຣໂຟນຂອງ​ທ່ານ​.</translation>
 <translation id="7617377681829253106">Chromium ​ດີກ​ວ່າແລ້ວ</translation>
 <translation id="7649070708921625228">ຊ່ວຍ​ເຫຼືອ</translation>
diff --git a/chrome/app/resources/chromium_strings_ml.xtb b/chrome/app/resources/chromium_strings_ml.xtb
index 6bd297fb..5c4315d 100644
--- a/chrome/app/resources/chromium_strings_ml.xtb
+++ b/chrome/app/resources/chromium_strings_ml.xtb
@@ -71,6 +71,8 @@
 <translation id="2174917724755363426">ഇൻസ്റ്റാൾ ചെയ്യൽ പൂർത്തിയായിട്ടില്ല. റദ്ദാക്കണമെന്ന് ഉറപ്പാണോ?</translation>
 <translation id="2185166961232948079">Chromium - നെറ്റ്‌വർക്ക് സൈൻ ഇൻ - <ph name="PAGE_TITLE" /></translation>
 <translation id="2190166659037789668">അപ്ഡേറ്റ് പരിശോധനയിലെ പിശക്: <ph name="UPDATE_CHECK_ERROR" />.</translation>
+<translation id="2210682093923538346">അപകടകരമായ സൈറ്റ്. അറിയിപ്പുകൾ Chromium നീക്കം ചെയ്തു.</translation>
+<translation id="2240214816234246077">നിങ്ങൾ അടുത്തിടെ സന്ദർശിച്ചിട്ടില്ല. <ph name="PERMISSION_1" />, <ph name="PERMISSION_2" /> എന്നീ അനുമതികൾ Chromium നീക്കം ചെയ്തു</translation>
 <translation id="2241627712206172106">നിങ്ങളൊരു കമ്പ്യൂട്ടർ പങ്കിടുകയാണെങ്കിൽ, സുഹൃത്തുക്കൾക്കും കുടുബാംഗങ്ങൾക്കും വെവ്വേറെ ബ്രൗസ് ചെയ്യാനും അവർക്ക് ആവശ്യമുള്ള രീതിയിൽ Chromium സജ്ജീകരിക്കാനുമാകും.</translation>
 <translation id="2313870531055795960">Chromium-ൽ സംഭരിച്ചിരിക്കുന്ന സുരക്ഷിതമല്ലാത്ത സൈറ്റുകളുടെ ലിസ്റ്റ് ഉപയോഗിച്ച് URL-കൾ പരിശോധിക്കുന്നു. ഒരു സൈറ്റ് നിങ്ങളുടെ പാസ്‌വേഡ് മോഷ്‌ടിക്കാൻ ശ്രമിക്കുമ്പോഴോ നിങ്ങൾ ഒരു ദോഷകരമായ ഫയൽ ഡൗൺലോഡ് ചെയ്യുമ്പോഴോ, പേജ് ഉള്ളടക്കത്തിന്റെ ബിറ്റുകൾ ഉൾപ്പെടെ URL-കൾ സുരക്ഷിത ബ്രൗസിംഗിലേക്ക് Chromium അയയ്ക്കുകയും ചെയ്തേക്കാം.</translation>
 <translation id="2329088755516916767">ഓണായിരിക്കുമ്പോൾ, ബ്രൗസ് ചെയ്യലും തിരയലും വേഗത്തിലാക്കുന്ന പേജുകൾ Chromium മുൻകൂട്ടി ലോഡ് ചെയ്യുന്നു.</translation>
@@ -269,6 +271,7 @@
 <translation id="5690427481109656848">Google LLC</translation>
 <translation id="5698481217667032250">ഈ ഭാഷയിൽ Chromium പ്രദർശിപ്പിക്കുക</translation>
 <translation id="569897634095159764">ഇന്റർനെറ്റിലേക്ക് കണക്റ്റ് ചെയ്യാനാകുന്നില്ല. പ്രോക്സി സെർവറിന് പരിശോധിച്ചുറപ്പിക്കൽ ആവശ്യമാണ്.</translation>
+<translation id="5761096224651992291">നിങ്ങൾ അടുത്തിടെ സന്ദർശിച്ചിട്ടില്ല. <ph name="PERMISSION_1" />, <ph name="PERMISSION_2" />, <ph name="PERMISSION_3" /> എന്നീ അനുമതികൾ Chromium നീക്കം ചെയ്തു</translation>
 <translation id="5800158606660203929">Chromium ഇഷ്‌ടാനുസൃതമാക്കുകയും നിയന്ത്രിക്കുകയും ചെയ്യൂ. Chromium ഡിഫോൾട്ടായി സജ്ജീകരിക്കുക.</translation>
 <translation id="5809516625706423866">ഇന്റർനെറ്റിലേക്ക് കണക്റ്റ് ചെയ്യാനാകുന്നില്ല. HTTP 401 അംഗീകൃതമല്ല. നിങ്ങളുടെ പ്രോക്‌സി കോൺഫിഗറേഷൻ പരിശോധിക്കുക.</translation>
 <translation id="5862307444128926510">Chromium-ത്തിലേക്ക് സ്വാഗതം</translation>
@@ -354,6 +357,7 @@
 <translation id="6729124504294600478">വ്യക്തിപരമാക്കലും മറ്റ് ഫീച്ചറുകളും ലഭിക്കാൻ, വെബ്, ആപ്പ് ആക്റ്റിവിറ്റിയിലും ലിങ്ക് ചെയ്‌ത Google സേവനങ്ങളിലും Chromium ഉൾപ്പെടുത്തുക</translation>
 <translation id="6734291798041940871">നിങ്ങളുടെ കമ്പ്യൂട്ടറിലെ എല്ലാ ഉപയോക്താക്കൾക്കുമായി നിലവിൽ Chromium ഇൻസ്റ്റാൾ ചെയ്‌തിട്ടുണ്ട്.</translation>
 <translation id="673636774878526923">നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങളിലും Chromium ഫയൽ ആക്സസ് ചെയ്യാൻ, സൈൻ ഇൻ ചെയ്ത ശേഷം സമന്വയിപ്പിക്കൽ ഓണാക്കുക.</translation>
+<translation id="674245979920622322">നിങ്ങൾ അടുത്തിടെ സന്ദർശിച്ചിട്ടില്ല. <ph name="PERMISSION_1" />, <ph name="PERMISSION_2" /> എന്നിവയും മറ്റ് <ph name="COUNT" /> അനുമതികളും Chromium നീക്കം ചെയ്തു</translation>
 <translation id="67706546131546258">ഈ ഫയൽ അപകടകരമാകാൻ സാധ്യതയുള്ളതിനാൽ ഇത് സ്‌കാൻ ചെയ്യാൻ Chromium നിർദ്ദേശിക്കുന്നു.</translation>
 <translation id="6779406956731413166">ChromiumOS സാധ്യമാക്കിയത് അധിക <ph name="BEGIN_LINK_CROS_OSS" />ഓപ്പൺ സോഴ്‌സ് സോഫ്റ്റ്‌വെയർ<ph name="END_LINK_CROS_OSS" /> ഉപയോഗിച്ചാണ്.</translation>
 <translation id="6847869444787758381">നിങ്ങളുടെ പാസ്‌വേഡുകൾ എപ്പോഴെങ്കിലും അപഹരിക്കപ്പെട്ടിട്ടുണ്ടെങ്കിൽ അക്കാര്യം Chromium നിങ്ങളെ അറിയിക്കും</translation>
@@ -406,6 +410,7 @@
 <translation id="753534427205733210">{0,plural, =1{ഒരു മിനിറ്റിൽ Chromium വീണ്ടും സമാരംഭിക്കും}other{# മിനിറ്റിൽ Chromium വീണ്ടും സമാരംഭിക്കും}}</translation>
 <translation id="7582945390259497898">Chromium-ന് നിങ്ങളുടെ താൽപ്പര്യങ്ങൾ നിർണ്ണയിക്കാനാകും. പിന്നീട്, കാണുന്ന പരസ്യങ്ങൾ വ്യക്തിപരമാക്കുന്നതിന് നിങ്ങളുടെ താൽപ്പര്യങ്ങൾ കാണിക്കാൻ നിങ്ങൾ സന്ദർശിക്കുന്ന സൈറ്റിന് Chromium-നോട് ആവശ്യപ്പെടാം.</translation>
 <translation id="7583399374488819119"><ph name="COMPANY_NAME" /> ഇൻസ്റ്റാളർ</translation>
+<translation id="7585391435984513350">നിങ്ങൾ അടുത്തിടെ സന്ദർശിച്ചിട്ടില്ല. <ph name="PERMISSION" /> Chromium നീക്കം ചെയ്തു</translation>
 <translation id="761356813943268536">Chromium നിങ്ങളുടെ ക്യാമറയും മൈക്രോഫോണും ഉപയോഗിക്കുന്നു.</translation>
 <translation id="7617377681829253106">Chromium കൂടുതൽ മികച്ചതായി</translation>
 <translation id="7649070708921625228">സഹായം</translation>
diff --git a/chrome/app/resources/generated_resources_lo.xtb b/chrome/app/resources/generated_resources_lo.xtb
index ca02917a..9096f481 100644
--- a/chrome/app/resources/generated_resources_lo.xtb
+++ b/chrome/app/resources/generated_resources_lo.xtb
@@ -465,6 +465,7 @@
 <translation id="1343865611738742294">ອະນຸຍາດໃຫ້ແອັບ Linux ເຂົ້າເຖິງອຸປະກອນ USB. Linux ຈະບໍ່ຈື່ອຸປະກອນ USB ຫຼັງຈາກທີ່ລຶບມັນອອກແລ້ວ.</translation>
 <translation id="1343920184519992513">ສືບຕໍ່ໜ້າເວັບທີ່ທ່ານເບິ່ງຄ້າງໄວ້ ແລະ ເປີດຊຸດໜ້າເວັບສະເພາະໃດໜຶ່ງ</translation>
 <translation id="1344141078024003905">ທ່ານກຳລັງສົ່ງສັນຍານໜ້າຈໍຂອງທ່ານ. ທ່ານສາມາດຢຸດຊົ່ວຄາວ ຫຼື ຢຸດການສົ່ງສັນຍານໜ້າຈໍຂອງທ່ານຕອນໃດກໍໄດ້.</translation>
+<translation id="1346403631707626730">ສົ່ງຂໍ້ມູນການນຳໃຊ້ ແລະ ການວິນິໄສ. ຊ່ວຍປັບປຸງປະສົບການໃນການໃຊ້ Android ຂອງລູກຂອງທ່ານໂດຍການສົ່ງຂໍ້ມູນການວິນິໄສ, ຂໍ້ມູນອຸປະກອນ ແລະ ຂໍ້ມູນການນຳໃຊ້ແອັບໄປໃຫ້ Google ໂດຍອັດຕະໂນມັດ. ຂໍ້ມູນນີ້ຈະບໍ່ຖືກໃຊ້ເພື່ອລະບຸຕົວຕົນຂອງລູກຂອງທ່ານ ແລະ ຈະຊ່ວຍປັບປຸງຄວາມສະຖຽນຂອງລະບົບ ແລະ ແອັບ ຮວມທັງການປັບປຸງດ້ານອື່ນໆ. ນອກຈາກນັ້ນ, ຂໍ້ມູນຮວມບາງສ່ວນຍັງຈະມີປະໂຫຍດຕໍ່ແອັບ ແລະ ຮຸ້ນສ່ວນຂອງ Google ນຳ ເຊັ່ນ: ນັກພັດທະນາ Android. ຫາກເປີດການເຄື່ອນໄຫວເວັບ ແລະ ແອັບເພີ່ມເຕີມສໍາລັບລູກຂອງທ່ານ, ລະບົບອາດບັນທຶກຂໍ້ມູນນີ້ໄວ້ໃນບັນຊີ Google ຂອງເຂົາເຈົ້າ. <ph name="BEGIN_LINK1" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການວັດແທກ<ph name="BEGIN_LINK1_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK1" /></translation>
 <translation id="1346630054604077329">ຢືນຢັນ ແລະ ຣີສະຕາດ</translation>
 <translation id="1346748346194534595">ເບື້ອງຂວາ</translation>
 <translation id="1347512539447549782">ບ່ອນຈັດເກັບຂໍ້ມູນ Linux</translation>
@@ -1936,6 +1937,7 @@
 <translation id="2405887402346713222">ໝາຍເລກຊີຣຽວອຸປະກອນ ແລະ ສ່ວນປະກອບ</translation>
 <translation id="2406153734066939945">ລຶບໂປຣໄຟລ໌ນີ້ ແລະ ຂໍ້ມູນຂອງມັນບໍ?</translation>
 <translation id="2407671304279211586">ເລືອກຜູ້ໃຫ້ບໍລິການ DNS</translation>
+<translation id="240789602312469910">ສົ່ງຂໍ້ມູນການນຳໃຊ້ ແລະ ການວິນິໄສ. ຊ່ວຍປັບປຸງປະສົບການໃນການໃຊ້ Android ຂອງລູກຂອງທ່ານໂດຍການສົ່ງຂໍ້ມູນການວິນິໄສ, ຂໍ້ມູນອຸປະກອນ ແລະ ຂໍ້ມູນການນຳໃຊ້ແອັບໄປໃຫ້ Google ໂດຍອັດຕະໂນມັດ. ຂໍ້ມູນນີ້ຈະບໍ່ຖືກໃຊ້ເພື່ອລະບຸຕົວຕົນຂອງລູກຂອງທ່ານ ແລະ ຈະຊ່ວຍປັບປຸງຄວາມສະຖຽນຂອງລະບົບ ແລະ ແອັບ ຮວມທັງການປັບປຸງດ້ານອື່ນໆ. ນອກຈາກນັ້ນ, ຂໍ້ມູນຮວມບາງສ່ວນຍັງຈະມີປະໂຫຍດຕໍ່ແອັບ ແລະ ຮຸ້ນສ່ວນຂອງ Google ນຳ ເຊັ່ນ: ນັກພັດທະນາ Android. ເຈົ້າຂອງເປັນຜູ້ບັງຄັບໃຊ້ <ph name="BEGIN_LINK1" />ການຕັ້ງຄ່າ<ph name="END_LINK1" /> ນີ້. ເຈົ້າຂອງອາດເລືອກທີ່ຈະສົ່ງຂໍ້ມູນການວິນິໄສ ແລະ ຂໍ້ມູນການນຳໃຊ້ສຳລັບອຸປະກອນນີ້ໄປໃຫ້ Google. ຫາກເປີດການຕັ້ງຄ່າການເຄື່ອນໄຫວເວັບ ແລະ ແອັບເພີ່ມເຕີມສໍາລັບລູກຂອງທ່ານ, ລະບົບອາດບັນທຶກຂໍ້ມູນນີ້ໄວ້ໃນບັນຊີ Google ຂອງເຂົາເຈົ້າ. <ph name="BEGIN_LINK2" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການວັດແທກ<ph name="BEGIN_LINK2_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK2" /></translation>
 <translation id="2408018932941436077">ກຳລັງບັນທຶກບັດ</translation>
 <translation id="2408955596600435184">​ປ້ອນ​ລະ​ຫັດ PIN ຂອງ​ທ່ານ</translation>
 <translation id="2409268599591722235">ເລີ່ມ​ກັນ​ເລີຍ</translation>
@@ -2096,6 +2098,7 @@
 <translation id="2497852260688568942">ການຊິ້ງຂໍ້ມູນຖືກປິດນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ</translation>
 <translation id="2498539833203011245">ຫຍໍ້ລົງ</translation>
 <translation id="2498765460639677199">ໃຫຍ່</translation>
+<translation id="2499065671910214321">ໃຊ້ສະຖານທີ່. ອະນຸຍາດໃຫ້ແອັບ, ເວັບໄຊ ແລະ ບໍລິການຂອງ ChromeOS ຮວມທັງ Android ທີ່ໄດ້ຮັບການອະນຸຍາດສະຖານທີ່ໃຊ້ສະຖານທີ່ຂອງອຸປະກອນນີ້. Google ອາດຮວບຮວມຂໍ້ມູນສະຖານທີ່ເປັນໄລຍະ ແລະ ໃຊ້ຂໍ້ມູນນີ້ແບບບໍ່ລະບຸຕົວຕົນເພື່ອປັບປຸງຄວາມຖືກຕ້ອງຂອງສະຖານທີ່ ແລະ ບໍລິການທີ່ອ້າງອີງສະຖານທີ່. <ph name="BEGIN_LINK1" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບສະຖານທີ່<ph name="BEGIN_LINK1_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK1" /></translation>
 <translation id="2500471369733289700">ບລັອກໄວ້ແລ້ວເພື່ອປົກປ້ອງຄວາມເປັນສ່ວນຕົວຂອງທ່ານ</translation>
 <translation id="2501173422421700905">ພັກໃບຢັ້ງຢືນຢູ່</translation>
 <translation id="2501278716633472235">ກັບ​ຄືນ​ໄປ</translation>
@@ -2359,6 +2362,7 @@
 <translation id="2704606927547763573">ອັດ​ສຳ​ເນົາ​ແລ້ວ</translation>
 <translation id="270516211545221798">ຄວາມໄວແຜ່ນສຳຜັດ</translation>
 <translation id="2705736684557713153">ເລື່ອນລົງສ່ວນລຸ່ມຂອງໜ້າຈໍ ແລະ ເປີດການປ່ອຍສັນຍານທັນທີ ຖ້າມັນປາກົດ. ຖ້າມັນບໍ່ປາກົດ ທ່ານຕັ້ງຄ່າຮຽບຮ້ອຍແລ້ວ.</translation>
+<translation id="2706304388244371417">ສຳຮອງຂໍ້ມູນໄປໃສ່ Google Drive. ກູ້ຄືນຂໍ້ມູນ ຫຼື ປ່ຽນອຸປະກອນໄດ້ຢ່າງງ່າຍດາຍທຸກເວລາ. ຂໍ້ມູນສຳຮອງນີ້ຈະຮວມທັງຂໍ້ມູນແອັບ. ລະບົບຈະອັບໂຫຼດຂໍ້ມູນສຳຮອງໄປໃສ່ Google ແລະ ເຂົ້າລະຫັດໂດຍໃຊ້ລະຫັດຜ່ານບັນຊີ Google ຂອງລູກຂອງທ່ານ. <ph name="BEGIN_LINK1" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການສຳຮອງຂໍ້ມູນ<ph name="BEGIN_LINK1_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK1" /></translation>
 <translation id="2707024448553392710">ການດາວ​ໂຫລດ​ອົງ​ປະ​ກອບ</translation>
 <translation id="270921614578699633">ສະເລ່ຍເກີນ</translation>
 <translation id="2709516037105925701">ຕື່ມອັດຕະໂນມັດ</translation>
@@ -2893,11 +2897,13 @@
 <translation id="3094521107841754472">ລາຄາໄດ້ປ່ຽນຈາກ <ph name="PREVIOUS_PRICE" /> ເປັນ <ph name="CURRENT_PRICE" /> ແລ້ວ.</translation>
 <translation id="3095871294753148861">ບຸກມາກ, ລະຫັດຜ່ານ ແລະ ຂໍ້ມູນໂປຣແກຣມທ່ອງເວັບອື່ນຖືກຊິ້ງຂໍ້ມູນກັບບັນຊີຫຼັກຂອງທ່ານ.</translation>
 <translation id="3099836255427453137">{NUM_EXTENSIONS,plural, =1{ສ່ວນຂະຫຍາຍທີ່ອາດຈະເປັນອັນຕະລາຍ 1 ລາຍການປິດຢູ່. ທ່ານສາມາດລຶບມັນອອກໄດ້ນຳ.}other{ສ່ວນຂະຫຍາຍທີ່ອາດຈະເປັນອັນຕະລາຍ {NUM_EXTENSIONS} ລາຍການປິດຢູ່. ທ່ານສາມາດລຶບມັນອອກໄດ້ນຳ.}}</translation>
+<translation id="3100071818310370858">ໃຊ້ສະຖານທີ່. ອະນຸຍາດໃຫ້ແອັບ ແລະ ບໍລິການທີ່ໄດ້ຮັບການອະນຸຍາດສະຖານທີ່ໃຊ້ສະຖານທີ່ຂອງອຸປະກອນນີ້. Google ອາດຮວບຮວມຂໍ້ມູນສະຖານທີ່ເປັນໄລຍະ ແລະ ໃຊ້ຂໍ້ມູນນີ້ໃນແບບບໍ່ລະບຸຕົວຕົນເພື່ອປັບປຸງຄວາມຖືກຕ້ອງຂອງສະຖານທີ່ ແລະ ບໍລິການທີ່ອ້າງອີງສະຖານທີ່. <ph name="BEGIN_LINK1" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບສະຖານທີ່<ph name="BEGIN_LINK1_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK1" /></translation>
 <translation id="3101126716313987672">ຫຼີ່ແສງລົງ</translation>
 <translation id="3101709781009526431">ວັນທີ ແລະເວລາ</translation>
 <translation id="310297983047869047">ສະໄລ້ກ່ອນໜ້າ</translation>
 <translation id="3103451787721578293">ກະລຸນາລະບຸເຫດຜົນສຳລັບການອັບໂຫຼດຂໍ້ມູນນີ້:</translation>
 <translation id="3103512663951238230">Alt + ຄລິກ</translation>
+<translation id="3104948640446684649">ສົ່ງຂໍ້ມູນການນຳໃຊ້ ແລະ ການວິນິໄສ. ປັດຈຸບັນອຸປະກອນນີ້ກຳລັງສົ່ງຂໍ້ມູນການວິນິໄສ, ຂໍ້ມູນອຸປະກອນ ແລະ ຂໍ້ມູນການນຳໃຊ້ແອັບໄປໃຫ້ Google ໂດຍອັດຕະໂນມັດ. ຂໍ້ມູນນີ້ຈະຊ່ວຍປັບປຸງຄວາມສະຖຽນຂອງລະບົບ ແລະ ແອັບ ຮວມທັງການປັບປຸງດ້ານອື່ນໆ. ນອກຈາກນັ້ນ, ຂໍ້ມູນຮວມບາງສ່ວນຍັງຈະມີປະໂຫຍດຕໍ່ແອັບ ແລະ ຮຸ້ນສ່ວນຂອງ Google ນຳ ເຊັ່ນ: ນັກພັດທະນາ Android. ຫາກເປີດການຕັ້ງຄ່າການເຄື່ອນໄຫວເວັບ ແລະ ແອັບເພີ່ມເຕີມຂອງທ່ານໄວ້, ລະບົບອາດບັນທຶກຂໍ້ມູນນີ້ໄວ້ໃນບັນຊີ Google ຂອງທ່ານ. <ph name="BEGIN_LINK2" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການວັດແທກ<ph name="BEGIN_LINK2_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK2" /></translation>
 <translation id="3105339775057145050">ອັບເດດບໍ່ສຳເລັດຫຼ້າສຸດ</translation>
 <translation id="3105796011181310544">ປ່ຽນກັບຄືນເປັນ Google ບໍ?</translation>
 <translation id="3105820656234755131">ອັບເດດລະຫັດຜ່ານແລ້ວ</translation>
@@ -2992,6 +2998,9 @@
 <translation id="3184536091884214176">ຕັ້ງຄ່າ ຫຼື ຈັດການເຄື່ອງພິມ CUPS. <ph name="LINK_BEGIN" />ສຶກສາເພີ່ມເຕີມ<ph name="LINK_END" /></translation>
 <translation id="3185014249447200271">{NUM_APPS,plural, =1{ແອັບນີ້ຖືກບລັອກໄວ້}other{ບາງແອັບຖືກບລັອກໄວ້}}</translation>
 <translation id="3187472288455401631">ການວັດແທກໂຄສະນາ</translation>
+<translation id="3187556136478864255">ທ່ານເຫັນອຸປະກອນ Chromecast ໃນ
+    <ph name="BEGIN_LINK" />
+    ແອັບ Google Home ຫຼືບໍ່<ph name="END_LINK" />?</translation>
 <translation id="3188465121994729530">ການ​​ຍ້າຍ​ໂດຍ​ສະ​ເລ່ຍ</translation>
 <translation id="3189187154924005138">ເຄີເຊີຂະໜາດໃຫຍ່</translation>
 <translation id="3190558889382726167">ບັນທຶກລະຫັດຜ່ານແລ້ວ</translation>
@@ -3246,6 +3255,7 @@
 <translation id="3391721320619127327">ໂປຣແກຣມອ່ານໜ້າຈໍຢູ່ ChromeOS Flex, ChromeVox, ແມ່ນໃຊ້ໂດຍຜູ້ພິການທາງສາຍຕາ ຫຼື ມີບັນຫາໃນການເບິ່ງເຫັນເປັນຫຼັກເພື່ອອ່ານຂໍ້ຄວາມທີ່ສະແດງຢູ່ໜ້າຈໍດ້ວຍຕົວສັງເຄາະສຽງເວົ້າ ຫຼື ຈໍສະແດງຜົນຕົວອັກສອນນູນ. ກົດປຸ່ມລະດັບສຽງທັງສອງປຸ່ມຄ້າງໄວ້ຫ້າວິນາທີເພື່ອເປີດ ChromeVox. ເມື່ອເປີດການນຳໃຊ້ ChromeVox, ລະບົບຈະແນະນຳການນຳໃຊ້ແບບສັ້ນໆໃຫ້ກັບທ່ານ.</translation>
 <translation id="3393554941209044235">ການວິເຄາະເອກະສານ Chrome</translation>
 <translation id="3393582007140394275">ບໍ່ສາມາດສົ່ງສັນຍານໜ້າຈໍໄດ້.</translation>
+<translation id="3394072120086516913">PC ໃຊ້ສາຍ ແລະ ອຸປະກອນ Chromecast ໃຊ້ Wi-Fi</translation>
 <translation id="3394850431319394743">ອະນຸຍາດໃຫ້ຕົວລະບຸຫຼິ້ນເນື້ອຫາທີ່ມີການປົກປ້ອງແລ້ວ</translation>
 <translation id="3396442984945202128">ຢັ້ງຢືນວ່າແມ່ນທ່ານ</translation>
 <translation id="3396800784455899911">ໂດຍການຄລິກປຸ່ມ "ຍອມຮັບ ແລະ ສືບຕໍ່", ແມ່ນຖືວ່າທ່ານເຫັນດີນຳການປະມວນຜົນຂໍ້ມູນທີ່ອະທິບາຍຢູ່ຂ້າງເທິງສຳລັບການບໍລິການ Google ເຫຼົ່ານີ້.</translation>
@@ -4504,6 +4514,7 @@
 <translation id="4314561087119792062">ເພີ່ມຊື່ໃໝ່ໃຫ້ຈຸດເຂົ້າເຖິງ</translation>
 <translation id="4314815835985389558">ຈັດການການຊິ້ງຂໍ້ມູນ</translation>
 <translation id="4316850752623536204">ເວັບ​ໄຊ​ທ​໌ຜູ້ພັດ​ທະ​ນາ​</translation>
+<translation id="43176328751044557">{NUM_SITES,plural, =1{ລຶບສິດການອະນຸຍາດສຳລັບ 1 ເວັບໄຊອອກແລ້ວ}other{ລຶບສິດການອະນຸຍາດສຳລັບ {NUM_SITES} ເວັບໄຊອອກແລ້ວ}}</translation>
 <translation id="4317733381297736564">ການຊື້ໃນແອັບ</translation>
 <translation id="4317820549299924617">ການຢັ້ງຢືນບໍ່ສຳເລັດ</translation>
 <translation id="4319441675152393296">ຄລິກໄອຄອນຂອງສ່ວນຂະຫຍາຍນີ້ເພື່ອອ່ານ ແລະ ປ່ຽນແປງ <ph name="HOST" /></translation>
@@ -4820,6 +4831,7 @@
 <translation id="4559617833001311418">ເວັບໄຊນີ້ກຳລັງເຂົ້າເຖິງເຊັນເຊີການເຄື່ອນໄຫວ ຫຼື ແສງຂອງທ່ານ.</translation>
 <translation id="4560728518401799797">ຕົວເລືອກເພີ່ມເຕີມສຳລັບບຸກມາກ <ph name="FOLDER_TITLE" /></translation>
 <translation id="4561893854334016293">ການອະນຸຍາດທີ່ປ່ຽນແປງເມື່ອບໍ່ດົນມານີ້</translation>
+<translation id="4562091353415772246">ຕິດຕັ້ງການອັບເດດ ແລະ ແອັບ. ໂດຍການສືບຕໍ່, ແມ່ນຖືວ່າທ່ານຍອມຮັບວ່າອຸປະກອນນີ້ອາດໃຊ້ອິນເຕີເນັດມືຖືດາວໂຫຼດ ແລະ ຕິດຕັ້ງການອັບເດດ ແລະ ແອັບຈາກ Google, ຜູ້ໃຫ້ບໍລິການຂອງທ່ານ ແລະ ຜູ້ຜະລິດອຸປະກອນຂອງທ່ານໂດຍອັດຕະໂນມັດນຳ. ແອັບເຫຼົ່ານີ້ບາງແອັບອາດສະເໜີການຊື້ໃນແອັບ. <ph name="BEGIN_LINK1" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການຕິດຕັ້ງອັດຕະໂນມັດຜ່ານ Play<ph name="BEGIN_LINK1_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK1" /></translation>
 <translation id="4562155214028662640">ເພີ່ມລາຍນິ້ວມື</translation>
 <translation id="4562155266774382038">ປິດຄຳແນະນຳ</translation>
 <translation id="4563210852471260509">ພາສາປ້ອນເຂົ້າເບື້ອງຕົ້ນແມ່ນພາສາຈີນ</translation>
@@ -5755,6 +5767,8 @@
 <translation id="5288106344236929384">ຄຳສັ່ງເພີ່ມເຕີມ, ຕົວເລືອກກະແຈຜ່ານສຳລັບ <ph name="USERNAME" /> ຢູ່ <ph name="DOMAIN" /></translation>
 <translation id="5288678174502918605">ເປີດແຖບທີ່ປິດແລ້ວຄືນໃໝ່</translation>
 <translation id="52895863590846877">ໜ້ານີ້ບໍ່ແມ່ນພາສາ <ph name="LANGUAGE" /></translation>
+<translation id="5290020561438336792">PC ແລະ ອຸປະກອນ Chromecast ໃຊ້ເຄືອຂ່າຍ Wi-Fi ຕ່າງກັນ (ເຊັ່ນ: 2.4GHz
+    ກັບ 5GHz)</translation>
 <translation id="52912272896845572">ໄຟລ໌ລະຫັດສ່ວນຕົວໃຊ້ບໍ່ໄດ້.</translation>
 <translation id="5291739252352359682">ສ້າງຄຳບັນຍາຍສຳລັບມີເດຍໃນໂປຣແກຣມທ່ອງເວັບ Chrome ໂດຍອັດຕະໂນມັດ (ຕອນນີ້ໃຊ້ໄດ້ແຕ່ພາສາອັງກິດເທົ່ານັ້ນ). ສຽງ ແລະ ຄຳບັນຍາຍແມ່ນຖືກປະມວນຜົນໃນເຄື່ອງ ແລະ ຈະບໍ່ຖືກສົ່ງອອກໄປຈາກອຸປະກອນເດັດຂາດ.</translation>
 <translation id="529175790091471945">ຟໍແມັດອຸ​ປະ​ກອນ​ນີ້</translation>
@@ -6237,9 +6251,12 @@
 <translation id="5646558797914161501">ນັກທຸລະກິດ</translation>
 <translation id="5648021990716966815">ແຈັກໄມໂຄຣໂຟນ</translation>
 <translation id="5648166631817621825">7 ມື້ທີ່ຜ່ານມາ</translation>
+<translation id="5649182603113127372">ໃຊ້ສະຖານທີ່. ອະນຸຍາດໃຫ້ແອັບ, ເວັບໄຊ ແລະ ບໍລິການຂອງ ChromeOS ຮວມທັງ Android ທີ່ໄດ້ຮັບການອະນຸຍາດສະຖານທີ່ໃຊ້ສະຖານທີ່ຂອງອຸປະກອນຂອງທ່ານ. Google ອາດຮວບຮວມຂໍ້ມູນສະຖານທີ່ເປັນໄລຍະ ແລະ ໃຊ້ຂໍ້ມູນນີ້ແບບບໍ່ລະບຸຕົວຕົນເພື່ອປັບປຸງຄວາມຖືກຕ້ອງຂອງສະຖານທີ່ ແລະ ບໍລິການທີ່ອ້າງອີງສະຖານທີ່. <ph name="BEGIN_LINK1" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບສະຖານທີ່<ph name="BEGIN_LINK1_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK1" /></translation>
+<translation id="5650537073531199882">ຕື່ມຂໍ້ມູນໃສ່ແບບຟອມ</translation>
 <translation id="5651308944918885595">ການຄົ້ນພົບການແບ່ງປັນໃກ້ຄຽງ</translation>
 <translation id="5653154844073528838">ທ່ານມີເຄື່ອງພິມທີ່ບັນທຶກໄວ້ <ph name="PRINTER_COUNT" /> ເຄື່ອງ.</translation>
 <translation id="5654669866168491665">ສຶກສາເພີ່ມເຕີມກ່ຽວກັບເວັບໄຊທີ່ອາດນຳໃຊ້ບໍ່ໄດ້ເມື່ອບລັອກຄຸກກີ້ພາກສ່ວນທີສາມ</translation>
+<translation id="5654848283274615843">{NUM_SITES,plural, =1{ເພື່ອປົກປ້ອງຄວາມເປັນສ່ວນຕົວຂອງທ່ານ, ລະບົບໄດ້ລຶບສິດການອະນຸຍາດອອກຈາກເວັບໄຊ}other{ເພື່ອປົກປ້ອງຄວາມເປັນສ່ວນຕົວຂອງທ່ານ, ລະບົບໄດ້ລຶບສິດການອະນຸຍາດອອກຈາກບາງເວັບໄຊ}}</translation>
 <translation id="565515993087783098">ໂດຍການລືມເຄືອຂ່າຍນີ້, ທ່ານຈະລຶບການສະໝັກໃຊ້ບໍລິການ Passpoint ແລະ ເຄືອຂ່າຍທີ່ກ່ຽວຂ້ອງຂອງມັນອອກນຳ.</translation>
 <translation id="5655296450510165335">ການລົງທະບຽນອຸປະກອນ</translation>
 <translation id="5655823808357523308">ປັບວິທີສະແດງສີຢູ່ໜ້າຈໍຂອງທ່ານ</translation>
@@ -6352,6 +6369,7 @@
 <translation id="5746169159649715125">ບັນທຶກເປັນ PDF</translation>
 <translation id="5747785204778348146">ຜູ້ພັດ​ທະ​ນາ - ບໍ່ໝັ້ນຄົງ</translation>
 <translation id="5747809636523347288">ວາງໃສ່ ແລະ ໄປຫາ <ph name="URL" /></translation>
+<translation id="5749214722697335450">ສົ່ງຂໍ້ມູນການນຳໃຊ້ ແລະ ການວິນິໄສ. ຊ່ວຍປັບປຸງປະສົບການໃນການໃຊ້ Android ຂອງທ່ານໂດຍການສົ່ງຂໍ້ມູນການວິນິໄສ, ຂໍ້ມູນອຸປະກອນ ແລະ ຂໍ້ມູນການນຳໃຊ້ແອັບໄປໃຫ້ Google ໂດຍອັດຕະໂນມັດ. ຂໍ້ມູນນີ້ຈະຊ່ວຍປັບປຸງຄວາມສະຖຽນຂອງລະບົບ ແລະ ແອັບ ຮວມທັງການປັບປຸງດ້ານອື່ນໆ. ນອກຈາກນັ້ນ, ຂໍ້ມູນຮວມບາງສ່ວນຍັງຈະມີປະໂຫຍດຕໍ່ແອັບ ແລະ ຮຸ້ນສ່ວນຂອງ Google ນຳ ເຊັ່ນ: ນັກພັດທະນາ Android. ຫາກເປີດການຕັ້ງຄ່າການເຄື່ອນໄຫວເວັບ ແລະ ແອັບເພີ່ມເຕີມຂອງທ່ານໄວ້, ລະບົບອາດບັນທຶກຂໍ້ມູນນີ້ໄວ້ໃນບັນຊີ Google ຂອງທ່ານ. <ph name="BEGIN_LINK1" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການວັດແທກ<ph name="BEGIN_LINK1_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK1" /></translation>
 <translation id="5750288053043553775">0</translation>
 <translation id="5751345516399502412">ກວດສອບຄວາມພ້ອມຂອງການປ່ອຍສັນຍານ</translation>
 <translation id="5753570386948603678">ລຶບອອກຈາກປະຫວັດ</translation>
@@ -6387,6 +6405,7 @@
     ການປິດການຕັ້ງຄ່ານີ້ຈະເຮັດໃຫ້ Android ໃຊ້ສະເພາະທີ່ຢູ່ IP ໃນການລະບຸສະຖານທີ່. ເຊິ່ງນີ້ອາດສົ່ງຜົນຕໍ່ຄວາມຖືກຕ້ອງຂອງສະຖານທີ່ເຊິ່ງແອັບຕ່າງໆນຳໃຊ້ ເຊັ່ນ: ແຜນທີ່.</translation>
 <translation id="5778491106820461378">ທ່ານສາມາດຈັດການບັນຊີ Google ທີ່ເຂົ້າສູ່ລະບົບແລ້ວໄດ້ຈາກ <ph name="LINK_BEGIN" />ການຕັ້ງຄ່າ<ph name="LINK_END" />. ການອະນຸຍາດທີ່ທ່ານມອບໃຫ້ເວັບໄຊ ແລະ ແອັບອາດຈະນຳໃຊ້ກັບບັນຊີທັງໝົດ. ຫາກທ່ານບໍ່ຕ້ອງການໃຫ້ເວັບໄຊ ຫຼື ແອັບເຂົ້າເຖິງຂໍ້ມູນບັນຊີຂອງທ່ານ, ທ່ານສາມາດເຂົ້າສູ່ລະບົບຫາ <ph name="DEVICE_TYPE" /> ຂອງທ່ານໃນຖານະແຂກໄດ້.</translation>
 <translation id="5780011244986845107">ໂຟນເດີທີ່ທ່ານເລືອກປະກອບມີໄຟລ໌ທີ່ລະອຽດອ່ອນ. ທ່ານແນ່ໃຈບໍ່ວ່າທ່ານຕ້ອງການໃຫ້ສິດເຂົ້າອ່ານຢ່າງຖາວອນໃນໂຟນເດີນີ້ແກ່ "<ph name="APP_NAME" />"?</translation>
+<translation id="5780940414249100901">ສົ່ງຂໍ້ມູນການນຳໃຊ້ ແລະ ການວິນິໄສ. ປັດຈຸບັນອຸປະກອນນີ້ກຳລັງສົ່ງຂໍ້ມູນການວິນິໄສ, ຂໍ້ມູນອຸປະກອນ ແລະ ຂໍ້ມູນການນຳໃຊ້ແອັບໄປໃຫ້ Google ໂດຍອັດຕະໂນມັດ. ຂໍ້ມູນນີ້ຈະບໍ່ຖືກໃຊ້ເພື່ອລະບຸຕົວຕົນຂອງລູກຂອງທ່ານ ແລະ ຈະຊ່ວຍປັບປຸງຄວາມສະຖຽນຂອງລະບົບ ແລະ ແອັບ ຮວມທັງການປັບປຸງດ້ານອື່ນໆ. ນອກຈາກນັ້ນ, ຂໍ້ມູນຮວມບາງສ່ວນຍັງຈະມີປະໂຫຍດຕໍ່ແອັບ ແລະ ຮຸ້ນສ່ວນຂອງ Google ນຳ ເຊັ່ນ: ນັກພັດທະນາ Android. ເຈົ້າຂອງເປັນຜູ້ບັງຄັບໃຊ້ <ph name="BEGIN_LINK1" />ການຕັ້ງຄ່າ<ph name="END_LINK1" /> ນີ້. ຫາກເປີດການຕັ້ງຄ່າການເຄື່ອນໄຫວເວັບ ແລະ ແອັບເພີ່ມເຕີມສໍາລັບລູກຂອງທ່ານ, ລະບົບອາດບັນທຶກຂໍ້ມູນນີ້ໄວ້ໃນບັນຊີ Google ຂອງເຂົາເຈົ້າ. <ph name="BEGIN_LINK2" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການວັດແທກ<ph name="BEGIN_LINK2_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK2" /></translation>
 <translation id="5780973441651030252">ບຸລິມະສິດຂອງຂະບວນການ</translation>
 <translation id="5781092003150880845">ຊິ້ງຂໍ້ມູນເປັນ <ph name="ACCOUNT_FULL_NAME" /></translation>
 <translation id="5781865261247219930">ສົ່ງຄຳສັ່ງຫາ <ph name="EXTENSION_NAME" /></translation>
@@ -7080,6 +7099,7 @@
 <translation id="6294759976468837022">ຄວາມໄວສະແກນອັດຕະໂນມັດ</translation>
 <translation id="6295158916970320988">ທຸກ​ເວັບ​ໄຊ​ທ໌</translation>
 <translation id="6295855836753816081">ກໍາລັງບັນທຶກ...</translation>
+<translation id="6297986260307280218">ສົ່ງຂໍ້ມູນການນຳໃຊ້ ແລະ ການວິນິໄສ. ຊ່ວຍປັບປຸງປະສົບການໃນການໃຊ້ Android ຂອງທ່ານໂດຍການສົ່ງຂໍ້ມູນການວິນິໄສ, ຂໍ້ມູນອຸປະກອນ ແລະ ຂໍ້ມູນການນຳໃຊ້ແອັບໄປໃຫ້ Google ໂດຍອັດຕະໂນມັດ. ຂໍ້ມູນນີ້ຈະຊ່ວຍປັບປຸງຄວາມສະຖຽນຂອງລະບົບ ແລະ ແອັບ ຮວມທັງການປັບປຸງດ້ານອື່ນໆ. ນອກຈາກນັ້ນ, ຂໍ້ມູນຮວມບາງສ່ວນຍັງຈະມີປະໂຫຍດຕໍ່ແອັບ ແລະ ຮຸ້ນສ່ວນຂອງ Google ນຳ ເຊັ່ນ: ນັກພັດທະນາ Android. ເຈົ້າຂອງເປັນຜູ້ບັງຄັບໃຊ້ <ph name="BEGIN_LINK1" />ການຕັ້ງຄ່າ<ph name="END_LINK1" /> ນີ້. ເຈົ້າຂອງອາດເລືອກທີ່ຈະສົ່ງຂໍ້ມູນການວິນິໄສ ແລະ ຂໍ້ມູນການນຳໃຊ້ສຳລັບອຸປະກອນນີ້ໄປໃຫ້ Google. ຫາກເປີດການຕັ້ງຄ່າການເຄື່ອນໄຫວເວັບ ແລະ ແອັບເພີ່ມເຕີມຂອງທ່ານໄວ້, ລະບົບອາດບັນທຶກຂໍ້ມູນນີ້ໄວ້ໃນບັນຊີ Google ຂອງທ່ານ. <ph name="BEGIN_LINK2" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການວັດແທກ<ph name="BEGIN_LINK2_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK2" /></translation>
 <translation id="6298456705131259420">ສົ່ງຜົນຕໍ່ເວັບໄຊທີ່ມີລາຍຊື່ຢູ່ບ່ອນນີ້. ການໃສ່ “[*.]” ກ່ອນຊື່ໂດເມນຈະສ້າງຂໍ້ຍົກເວັ້ນສໍາລັບໂດເມນທັງໝົດ. ຕົວຢ່າງ: ການເພີ່ມ “[*.]google.com” ໝາຍຄວາມວ່າຄຸກກີ້ພາກສ່ວນທີສາມຈະສາມາດໃຊ້ໄດ້ກັບ mail.google.com ນຳ, ເນື່ອງຈາກມັນເປັນສ່ວນໜຶ່ງຂອງ google.com.</translation>
 <translation id="6298962879096096191">ໃຊ້ Google Play ເພື່ອຕິດຕັ້ງແອັບ Android</translation>
 <translation id="6300177430812514606">ບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ເຮັດສຳເລັດການສົ່ງ ຫຼື ຮັບຂໍ້ມູນ</translation>
@@ -8119,6 +8139,7 @@
 <translation id="710047887584828070">ເນື້ອຫາຂອງແຖບນີ້ກຳລັງຖືກແບ່ງປັນຢູ່</translation>
 <translation id="710224247908684995">ສ່ວນຂະຫຍາຍໜຶ່ງໄດ້ປິດ Safe Browsing</translation>
 <translation id="7102832101143475489">ຄຳຂໍໝົດເວລາແລ້ວ</translation>
+<translation id="7103944802169726298">PC ແລະ ອຸປະກອນ Chromecast ໃຊ້ເຄືອຂ່າຍ Wi-Fi ດຽວກັນ</translation>
 <translation id="710640343305609397">ເປີດການຕັ້ງຄ່າເຄືອຂ່າຍ</translation>
 <translation id="7107609441453408294">ຫຼິ້ນສຽງດຽວກັນຜ່ານທຸກລຳໂພງ</translation>
 <translation id="7108338896283013870">ເຊື່ອງ</translation>
@@ -10269,10 +10290,12 @@
 <translation id="8695139659682234808">ເພີ່ມການຄວບຄຸມຂອງພໍ່ແມ່ຫຼັງຈາກການຕັ້ງຄ່າ</translation>
 <translation id="8695825812785969222">ເປີດ​​ທີ່ຕັ້ງ...</translation>
 <translation id="8698269656364382265">ປັດຈາກເບື້ອງຊ້າຍເພື່ອກັບຄືນຫາໜ້າຈໍຜ່ານມາ.</translation>
+<translation id="8698432579173128320">ສຳຮອງຂໍ້ມູນໄປໃສ່ Google Drive. ກູ້ຄືນຂໍ້ມູນຂອງທ່ານ ຫຼື ປ່ຽນອຸປະກອນໄດ້ຢ່າງງ່າຍດາຍທຸກເວລາ. ຂໍ້ມູນສຳຮອງຂອງທ່ານຈະຮວມທັງຂໍ້ມູນແອັບ. ລະບົບຈະອັບໂຫຼດຂໍ້ມູນສຳຮອງຂອງທ່ານໄປໃສ່ Google ແລະ ເຂົ້າລະຫັດໂດຍໃຊ້ລະຫັດຜ່ານບັນຊີ Google ຂອງທ່ານ. <ph name="BEGIN_LINK1" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການສຳຮອງຂໍ້ມູນ<ph name="BEGIN_LINK1_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK1" /></translation>
 <translation id="869884720829132584">ເມ​ນູ​ແອັບພລິເຄຊັນ</translation>
 <translation id="869891660844655955">ວັນ​ຫມົດ​ອາ​ຍຸ</translation>
 <translation id="8699188901396699995">PPD ສຳລັບ <ph name="PRINTER_NAME" /></translation>
 <translation id="8700066369485012242">ແຈ້ງໃຫ້ພວກເຮົາຮູ້ເຫດຜົນທີ່ທ່ານໄດ້ອະນຸຍາດຄຸກກີ້ພາກສ່ວນທີສາມໃນເວັບໄຊນີ້</translation>
+<translation id="8700087567921985940">ໃຊ້ສະຖານທີ່. ອະນຸຍາດໃຫ້ແອັບ ແລະ ບໍລິການທີ່ໄດ້ຮັບການອະນຸຍາດສະຖານທີ່ໃຊ້ສະຖານທີ່ຂອງອຸປະກອນຂອງທ່ານ. Google ອາດຮວບຮວມຂໍ້ມູນສະຖານທີ່ເປັນໄລຍະ ແລະ ໃຊ້ຂໍ້ມູນນີ້ໃນແບບບໍ່ລະບຸຕົວຕົນເພື່ອປັບປຸງຄວາມຖືກຕ້ອງຂອງສະຖານທີ່ ແລະ ບໍລິການທີ່ອ້າງອີງສະຖານທີ່. <ph name="BEGIN_LINK1" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບສະຖານທີ່<ph name="BEGIN_LINK1_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK1" /></translation>
 <translation id="8700416429250425628">ຕົວເປີດໃຊ້ + ປຸ່ມ backspace</translation>
 <translation id="8702278591052316269">ເມນູມີກຸ່ມແຖບທີ່ບັນທຶກໄວ້ທີ່ເຊື່ອງໄວ້</translation>
 <translation id="8702825062053163569">ລັອກ <ph name="DEVICE_TYPE" /> ຂອງທ່ານໄວ້ແລ້ວ.</translation>
@@ -10525,6 +10548,7 @@
 <translation id="8881020143150461183">ກະລຸນາລອງໃໝ່. ສຳລັບການຊ່ວຍເຫຼືອດ້ານເທັກນິກ, ກະລຸນາຕິດຕໍ່ <ph name="CARRIER_NAME" />.</translation>
 <translation id="888256071122006425">ການຕັ້ງຄ່າເມົ້າ ແລະ ແຜ່ນສໍາຜັດ</translation>
 <translation id="8883273463630735858">ເປີດນຳໃຊ້ການເລັ່ງຄວາມໄວແຜ່ນສຳຜັດ</translation>
+<translation id="8883478023074930307">ສົ່ງຂໍ້ມູນການນຳໃຊ້ ແລະ ການວິນິໄສ. ປັດຈຸບັນອຸປະກອນນີ້ກຳລັງສົ່ງຂໍ້ມູນການວິນິໄສ, ຂໍ້ມູນອຸປະກອນ ແລະ ຂໍ້ມູນການນຳໃຊ້ແອັບໄປໃຫ້ Google ໂດຍອັດຕະໂນມັດ. ຂໍ້ມູນນີ້ຈະຊ່ວຍປັບປຸງຄວາມສະຖຽນຂອງລະບົບ ແລະ ແອັບ ຮວມທັງການປັບປຸງດ້ານອື່ນໆ. ນອກຈາກນັ້ນ, ຂໍ້ມູນຮວມບາງສ່ວນຍັງຈະມີປະໂຫຍດຕໍ່ແອັບ ແລະ ຮຸ້ນສ່ວນຂອງ Google ນຳ ເຊັ່ນ: ນັກພັດທະນາ Android. ເຈົ້າຂອງເປັນຜູ້ບັງຄັບໃຊ້ <ph name="BEGIN_LINK1" />ການຕັ້ງຄ່າ<ph name="END_LINK1" /> ນີ້. ຫາກເປີດການຕັ້ງຄ່າການເຄື່ອນໄຫວເວັບ ແລະ ແອັບເພີ່ມເຕີມຂອງທ່ານໄວ້, ລະບົບອາດບັນທຶກຂໍ້ມູນນີ້ໄວ້ໃນບັນຊີ Google ຂອງທ່ານ. <ph name="BEGIN_LINK2" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການວັດແທກ<ph name="BEGIN_LINK2_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK2" /></translation>
 <translation id="8884023684057697730"><ph name="BEGIN_BOLD" />ທ່ານຈະຈັດການຂໍ້ມູນຂອງທ່ານແນວໃດ:<ph name="END_BOLD" /> ເພື່ອປົກປ້ອງຄວາມເປັນສ່ວນຕົວຂອງທ່ານ, ພວກເຮົາຈະລຶບເວັບໄຊອອກຈາກລາຍຊື່ທີ່ເກົ່າກວ່າ 4 ອາທິດໂດຍອັດຕະໂນມັດ. ເວັບໄຊທີ່ທ່ານເຂົ້າອາດປາກົດຢູ່ລາຍຊື່ຄືນໃໝ່. ຫຼື ທ່ານສາມາດລຶບເວັບໄຊໃດໜຶ່ງອອກໄດ້ຫາກທ່ານບໍ່ຕ້ອງການໃຫ້ເວັບໄຊນັ້ນກຳນົດຄວາມສົນໃຈໃຫ້ທ່ານອີກ.</translation>
 <translation id="8884570509232205463">ອຸປະກອນຂອງທ່ານຕອນນີ້ຈະລັອກເວລາ <ph name="UNLOCK_TIME" /></translation>
 <translation id="8885449336974696155">ຊ່ວຍເຮັດໃຫ້ ChromeOS ດີຂຶ້ນໂດຍການລາຍງານ <ph name="BEGIN_LINK" />ການຕັ້ງຄ່າປັດຈຸບັນ<ph name="END_LINK" /></translation>
@@ -10954,6 +10978,7 @@
 <translation id="919686179725692564">ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການສຳຮອງຂໍ້ມູນແອັບຂອງທ່ານ</translation>
 <translation id="9199503643457729322">ຄລິກເພື່ອອອກຈາກຂໍ້ແນະນຳຄວາມເປັນສ່ວນຕົວ.</translation>
 <translation id="9199695835892108985">ຣີເຊັດການອະນຸຍາດຜອດ Serial ທັງໝົດບໍ?</translation>
+<translation id="9199853905755292769">ສົ່ງຂໍ້ມູນການນຳໃຊ້ ແລະ ການວິນິໄສ. ປັດຈຸບັນອຸປະກອນນີ້ກຳລັງສົ່ງຂໍ້ມູນການວິນິໄສ, ຂໍ້ມູນອຸປະກອນ ແລະ ຂໍ້ມູນການນຳໃຊ້ແອັບໄປໃຫ້ Google ໂດຍອັດຕະໂນມັດ. ຂໍ້ມູນນີ້ຈະບໍ່ຖືກໃຊ້ເພື່ອລະບຸຕົວຕົນຂອງລູກຂອງທ່ານ ແລະ ຈະຊ່ວຍປັບປຸງຄວາມສະຖຽນຂອງລະບົບ ແລະ ແອັບ ຮວມທັງການປັບປຸງດ້ານອື່ນໆ. ນອກຈາກນັ້ນ, ຂໍ້ມູນຮວມບາງສ່ວນຍັງຈະມີປະໂຫຍດຕໍ່ແອັບ ແລະ ຮຸ້ນສ່ວນຂອງ Google ນຳ ເຊັ່ນ: ນັກພັດທະນາ Android. ຫາກເປີດການຕັ້ງຄ່າການເຄື່ອນໄຫວເວັບ ແລະ ແອັບເພີ່ມເຕີມສໍາລັບລູກຂອງທ່ານ, ລະບົບອາດບັນທຶກຂໍ້ມູນນີ້ໄວ້ໃນບັນຊີ Google ຂອງເຂົາເຈົ້າ. <ph name="BEGIN_LINK2" />ສຶກສາເພີ່ມເຕີມກ່ຽວກັບການວັດແທກ<ph name="BEGIN_LINK2_END" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK2" /></translation>
 <translation id="9200339982498053969"><ph name="ORIGIN" /> ຈະສາມາດແກ້ໄຂໄຟລ໌ໃນ <ph name="FOLDERNAME" /> ໄດ້</translation>
 <translation id="920045321358709304">ຊອກຫາ <ph name="SEARCH_ENGINE" /></translation>
 <translation id="9201117361710210082">ເບິ່ງແລ້ວກ່ອນໜ້າ</translation>
diff --git a/chrome/app/resources/generated_resources_ml.xtb b/chrome/app/resources/generated_resources_ml.xtb
index f06294b..f7a67a8 100644
--- a/chrome/app/resources/generated_resources_ml.xtb
+++ b/chrome/app/resources/generated_resources_ml.xtb
@@ -465,6 +465,7 @@
 <translation id="1343865611738742294">USB ഉപകരണങ്ങൾ ആക്‌സസ് ചെയ്യാൻ Linux ആപ്പുകൾക്ക് അനുമതി നൽകുക. USB ഉപകരണം നീക്കം ചെയ്‌തുകഴിഞ്ഞാൽ Linux അത് ഓർക്കില്ല.</translation>
 <translation id="1343920184519992513">നിങ്ങൾ നിർത്തിയ ഇടത്ത് നിന്ന് തുടരുക, പേജുകളുടെ നിർദ്ദിഷ്ട സെറ്റ് തുറക്കുക</translation>
 <translation id="1344141078024003905">നിങ്ങൾ സ്ക്രീൻ കാസ്റ്റ് ചെയ്യുകയാണ്. നിങ്ങൾക്ക് ഏതുസമയത്തും സ്ക്രീൻ കാസ്റ്റ് ചെയ്യുന്നത് നിർത്തുകയോ താൽക്കാലികമായി നിർത്തുകയോ ചെയ്യാം.</translation>
+<translation id="1346403631707626730">ഉപയോഗവും പ്രശ്നനിർണ്ണയവുമായി ബന്ധപ്പെട്ട ഡാറ്റ അയയ്ക്കുക. പ്രശ്‌നനിർണ്ണയം, ഉപകരണം, ആപ്പ് ഉപയോഗം എന്നിവയുമായി ബന്ധപ്പെട്ട ഡാറ്റ സ്വയമേവ Google-ന് അയച്ച്, കുട്ടിയുടെ Android അനുഭവം മെച്ചപ്പെടുത്താൻ സഹായിക്കുക. നിങ്ങളുടെ കുട്ടിയെ തിരിച്ചറിയാൻ ഇത് ഉപയോഗിക്കില്ല, സിസ്‌റ്റം, ആപ്പ് സ്ഥിരത, മറ്റ് മെച്ചപ്പെടുത്തലുകൾ എന്നിവയ്ക്ക് സഹായിക്കുകയും ചെയ്യും. Google ആപ്പുകളെയും Android ഡെവലപ്പർമാരെപ്പോലുള്ള പങ്കാളികളെയും ചില സംയോജിത ഡാറ്റ സഹായിക്കും. കുട്ടിയുടെ അധിക വെബ്, ആപ്പ് ആക്‌റ്റിവിറ്റി ഓണാക്കിയിട്ടുണ്ടെങ്കിൽ, ഈ ഡാറ്റ അവരുടെ Google Account-ൽ സംരക്ഷിക്കപ്പെട്ടേക്കാം. <ph name="BEGIN_LINK1" />മെട്രിക്സിനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK1_END" />കൂടുതലറിയുക<ph name="END_LINK1" /></translation>
 <translation id="1346630054604077329">സ്ഥിരീകരിച്ച് റീസ്‌റ്റാർട്ട് ചെയ്യുക</translation>
 <translation id="1346748346194534595">ശരി</translation>
 <translation id="1347512539447549782">Linux സ്‌റ്റോറേജ്</translation>
@@ -1921,6 +1922,7 @@
 <translation id="2405887402346713222">ഉപകരണം, കമ്പോണന്റ് എന്നിവയുടെ സീരിയൽ നമ്പറുകൾ</translation>
 <translation id="2406153734066939945">ഈ പ്രൊഫൈലും ഇതിലെ ഡാറ്റയും ഇല്ലാതാക്കണോ?</translation>
 <translation id="2407671304279211586">DNS ദാതാവിനെ തിരഞ്ഞെടുക്കുക</translation>
+<translation id="240789602312469910">ഉപയോഗവും പ്രശ്‌നനിർണ്ണയവുമായി ബന്ധപ്പെട്ട ഡാറ്റ അയയ്ക്കുക. പ്രശ്‌നനിർണ്ണയം, ഉപകരണം, ആപ്പ് ഉപയോഗം എന്നിവയുമായി ബന്ധപ്പെട്ട ഡാറ്റ സ്വയമേവ Google-ന് അയച്ച്, കുട്ടിയുടെ Android അനുഭവം മെച്ചപ്പെടുത്താൻ സഹായിക്കുക. നിങ്ങളുടെ കുട്ടിയെ തിരിച്ചറിയാൻ ഇത് ഉപയോഗിക്കില്ല, സിസ്‌റ്റം, ആപ്പ് സ്ഥിരത, മറ്റ് മെച്ചപ്പെടുത്തലുകൾ എന്നിവയ്ക്ക് സഹായിക്കുകയും ചെയ്യും. ചില സംഗ്രഹ ഡാറ്റ, Google ആപ്പുകളെയും Android ഡെവലപ്പർമാരെപ്പോലുള്ള പങ്കാളികളെയും സഹായിക്കുകയും ചെയ്യും. ഈ <ph name="BEGIN_LINK1" />ക്രമീകരണം<ph name="END_LINK1" /> നടപ്പിലാക്കിയിരിക്കുന്നത് ഉടമയാണ്. ഈ ഉപകരണത്തിലെ പ്രശ്‌നനിർണ്ണയവും ഉപയോഗവുമായി ബന്ധപ്പെട്ട ഡാറ്റ Google-ന് അയയ്ക്കാൻ ഉടമ തീരുമാനിച്ചേക്കാം. നിങ്ങളുടെ കുട്ടിയുടെ അധിക വെബ്, ആപ്പ് ആക്റ്റിവിറ്റി ക്രമീകരണം ഓണാക്കിയിട്ടുണ്ടെങ്കിൽ, ഈ ഡാറ്റ അവരുടെ Google അക്കൗണ്ടിൽ സംരക്ഷിക്കപ്പെട്ടേക്കാം. <ph name="BEGIN_LINK2" />മെട്രിക്സിനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK2_END" />കൂടുതലറിയുക<ph name="END_LINK2" /></translation>
 <translation id="2408018932941436077">കാർഡ് സംരക്ഷിക്കുന്നു</translation>
 <translation id="2408955596600435184">നിങ്ങളുടെ പിൻ നൽകുക</translation>
 <translation id="2409268599591722235">നമുക്ക് ആരംഭിക്കാം</translation>
@@ -2081,6 +2083,7 @@
 <translation id="2497852260688568942">നിങ്ങളുടെ അഡ്‌മിനിസ്‌ട്രേറ്റർ സമന്വയിപ്പിക്കൽ പ്രവർത്തനരഹിതമാക്കി</translation>
 <translation id="2498539833203011245">ചെറുതാക്കുക</translation>
 <translation id="2498765460639677199">ബൃഹത്തായ</translation>
+<translation id="2499065671910214321">ലൊക്കേഷൻ ഉപയോഗിക്കുക. ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ഉപയോഗിക്കാൻ, ലൊക്കേഷൻ അനുമതിയുള്ള ChromeOS, Android ആപ്പുകൾ, വെബ്‌സൈറ്റുകൾ, സേവനങ്ങൾ എന്നിവയെ അനുവദിക്കുക. ലൊക്കേഷൻ കൃത്യതയും ലൊക്കേഷൻ അധിഷ്‌ഠിത സേവനങ്ങളും മെച്ചപ്പെടുത്താൻ, Google കാലാകാലങ്ങളിൽ ലൊക്കേഷൻ ഡാറ്റ ശേഖരിക്കുകയും നിങ്ങളുടെ വ്യക്തിവിവരങ്ങൾ എടുത്തുനീക്കി അവ ഉപയോഗിക്കുകയും ചെയ്‌തേക്കാം. <ph name="BEGIN_LINK1" />ലൊക്കേഷനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK1_END" />കൂടുതലറിയുക<ph name="END_LINK1" /></translation>
 <translation id="2500471369733289700">നിങ്ങളുടെ സ്വകാര്യത സംരക്ഷിക്കുന്നതിനായി ബ്ലോക്കുചെയ്‌‌തു</translation>
 <translation id="2501173422421700905">സര്‍‌ട്ടിഫിക്കറ്റ് തടഞ്ഞിരിക്കുന്നു</translation>
 <translation id="2501278716633472235">പിന്നോട്ട് പോകുക</translation>
@@ -2344,6 +2347,7 @@
 <translation id="2704606927547763573">പകർത്തി</translation>
 <translation id="270516211545221798">ടച്ച്പാഡിന്റെ വേഗത</translation>
 <translation id="2705736684557713153">സ്‌ക്രീനിന്റെ താഴേക്ക് സ്‍ക്രോള്‍ ചെയ്യുകയും, തൽക്ഷണ ടെതറിംഗ് ദൃശ്യമാകുന്നെങ്കിൽ അത് ഓണാക്കുകയും ചെയ്യുക. അത് ദൃശ്യമാകുന്നില്ലെങ്കിൽ, നിങ്ങൾ സജ്ജമായി.</translation>
+<translation id="2706304388244371417">Google Drive-ലേക്ക് ബാക്കപ്പ് ചെയ്യുക. എളുപ്പത്തിൽ ഡാറ്റ പുനഃസ്ഥാപിക്കുകയോ ഏതുസമയത്തും ഉപകരണം മാറുകയോ ചെയ്യുക. ഈ ബാക്കപ്പിൽ ആപ്പ് ഡാറ്റയും ഉൾപ്പെടുന്നു. ബാക്കപ്പുകൾ Google-ലേക്ക് അപ്‌ലോഡ് ചെയ്‌ത്, നിങ്ങളുടെ കുട്ടിയുടെ Google Account പാസ്‌വേഡ് ഉപയോഗിച്ച് എൻക്രിപ്റ്റ് ചെയ്യും. <ph name="BEGIN_LINK1" />ബാക്കപ്പിനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK1_END" />കൂടുതലറിയുക<ph name="END_LINK1" /></translation>
 <translation id="2707024448553392710">ഘടക ഭാഗം ഡൗൺലോഡുചെയ്യുന്നു</translation>
 <translation id="270921614578699633">ശരാശരിയ്ക്കും മുകളിൽ</translation>
 <translation id="2709516037105925701">സ്വയമേവ പൂരിപ്പിക്കൽ</translation>
@@ -2878,11 +2882,13 @@
 <translation id="3094521107841754472">വില <ph name="PREVIOUS_PRICE" /> എന്നതിൽ നിന്ന് <ph name="CURRENT_PRICE" /> ആയി മാറി.</translation>
 <translation id="3095871294753148861">ബുക്ക്‌മാര്‍‌ക്കുകളും പാസ്‌വേഡുകളും മറ്റ് ബ്രൗസർ ഡാറ്റയും പ്രാഥമിക അക്കൗണ്ടുമായി സമന്വയിപ്പിക്കുന്നു.</translation>
 <translation id="3099836255427453137">{NUM_EXTENSIONS,plural, =1{ദോഷകരമാകാൻ സാധ്യതയുള്ള ഒരു വിപുലീകരണം ഓഫാണ്. നിങ്ങൾക്ക് ഇത് നീക്കം ചെയ്യാനും കഴിയും.}other{ദോഷകരമാകാൻ സാധ്യതയുള്ള {NUM_EXTENSIONS} വിപുലീകരണങ്ങൾ ഓഫാണ്. നിങ്ങൾക്ക് ഇവ നീക്കം ചെയ്യാനും കഴിയും.}}</translation>
+<translation id="3100071818310370858">ലൊക്കേഷൻ ഉപയോഗിക്കുക. ലൊക്കേഷൻ അനുമതിയുള്ള ആപ്പുകളെയും സേവനങ്ങളെയും ഈ ഉപകരണത്തിന്‍റെ ലൊക്കേഷൻ ഉപയോഗിക്കാൻ അനുവദിക്കുക. Google, ഇടയ്ക്കിടെ ലൊക്കേഷൻ ഡാറ്റ ശേഖരിക്കുകയും, ലൊക്കേഷൻ കൃത്യതയും ലൊക്കേഷൻ അധിഷ്ഠിത സേവനങ്ങളും മെച്ചപ്പെടുത്താൻ, നിങ്ങളുടെ വ്യക്തിവിവരങ്ങൾ എടുത്തുനീക്കി ഈ ഡാറ്റ ഉപയോഗിക്കുകയും ചെയ്തേക്കാം. <ph name="BEGIN_LINK1" />ലൊക്കേഷനെ കുറിച്ച് കുടുതലറിയുക<ph name="BEGIN_LINK1_END" />കൂടുതലറിയുക<ph name="END_LINK1" /></translation>
 <translation id="3101126716313987672">മങ്ങിയ വെളിച്ചം</translation>
 <translation id="3101709781009526431">തീയതിയും സമയവും</translation>
 <translation id="310297983047869047">മുമ്പത്തെ സ്ലൈഡ്</translation>
 <translation id="3103451787721578293">ഈ ഡാറ്റ അപ്‌ലോഡ് ചെയ്യുന്നതിന് കാരണം നൽകുക:</translation>
 <translation id="3103512663951238230">Alt + ക്ലിക്ക്</translation>
+<translation id="3104948640446684649">ഉപയോഗവും പ്രശ്‌നനിർണ്ണയവുമായി ബന്ധപ്പെട്ട ഡാറ്റ അയയ്ക്കുക. പ്രശ്‌നനിർണ്ണയം, ഉപകരണം, ആപ്പ് ഉപയോഗം എന്നിവയുമായി ബന്ധപ്പെട്ട ഡാറ്റ, ഈ ഉപകരണം നിലവിൽ സ്വയമേവ Google-ന് അയയ്ക്കുന്നുണ്ട്. സിസ്‌റ്റം, ആപ്പ് സ്ഥിരത, മറ്റ് മെച്ചപ്പെടുത്തലുകൾ എന്നിവയ്ക്ക് ഇത് സഹായിക്കും. ചില സംഗ്രഹ ഡാറ്റ, Google ആപ്പുകളെയും Android ഡെവലപ്പർമാരെപ്പോലുള്ള പങ്കാളികളെയും സഹായിക്കുകയും ചെയ്യും. നിങ്ങളുടെ അധിക വെബ്, ആപ്പ് ആക്റ്റിവിറ്റി ക്രമീകരണം ഓണാക്കിയിട്ടുണ്ടെങ്കിൽ, ഈ ഡാറ്റ നിങ്ങളുടെ Google അക്കൗണ്ടിൽ സംരക്ഷിക്കപ്പെട്ടേക്കാം. <ph name="BEGIN_LINK2" />മെട്രിക്സിനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK2_END" />കൂടുതലറിയുക<ph name="END_LINK2" /></translation>
 <translation id="3105339775057145050">പരാജയപ്പെട്ട അവസാന അപ്‌ഡേറ്റ്</translation>
 <translation id="3105796011181310544">തിരികെ Google-ലേക്ക് മാറ്റണോ?</translation>
 <translation id="3105820656234755131">പാസ്‌വേഡ് അപ്ഡേറ്റ് ചെയ്‌തു</translation>
@@ -2977,6 +2983,9 @@
 <translation id="3184536091884214176">CUPS പ്രിന്ററുകൾ സജ്ജമാക്കുക അല്ലെങ്കിൽ മാനേജ് ചെയ്യുക. <ph name="LINK_BEGIN" />കൂടുതലറിയുക<ph name="LINK_END" /></translation>
 <translation id="3185014249447200271">{NUM_APPS,plural, =1{ഈ ആപ്പ് ബ്ലോക്ക് ചെയ്‌തു}other{ചില ആപ്പുകൾ ബ്ലോക്ക് ചെയ്‌തു}}</translation>
 <translation id="3187472288455401631">ആഡ് മെഷർമെന്റ്</translation>
+<translation id="3187556136478864255">നിങ്ങളുടെ കാസ്റ്റിംഗ് ഉപകരണം
+    <ph name="BEGIN_LINK" />
+    Google Home ആപ്പിൽ<ph name="END_LINK" /> കാണാൻ കഴിയുന്നുണ്ടോ?</translation>
 <translation id="3188465121994729530">ശരാശരി നീക്കുന്നു</translation>
 <translation id="3189187154924005138">വലിയ കഴ്‌സർ</translation>
 <translation id="3190558889382726167">പാസ്‌വേഡ് സംരക്ഷിച്ചു</translation>
@@ -3231,6 +3240,7 @@
 <translation id="3391721320619127327">സംഭാഷണ സിന്തസൈസർ അല്ലെങ്കിൽ ബ്രെയ്‌ലി ഡിസ്‌പ്ലേയുടെ സഹായത്തോടെ സ്ക്രീനിൽ കാണിക്കുന്ന ടെക്‌സ്റ്റ് വായിക്കാൻ അന്ധതയോ കാഴ്ചക്കുറവോ ഉള്ള ആളുകളാണ് ChromeOS Flex, ChromeVox എന്നിവയിലെ സ്ക്രീൻ പ്രധാനമായും റീഡർ ഉപയോഗിക്കുന്നത്. ChromeVox ഓണാക്കാൻ രണ്ട് വോളിയം കീകളും അഞ്ച് സെക്കൻഡ് നേരത്തേക്ക് അമർത്തിപ്പിടിക്കുക. ChromeVox സജീവമായിക്കഴിഞ്ഞാൽ, ദ്രുത ടൂർ വഴി നിങ്ങൾക്ക് അതേക്കുറിച്ച് അറിയാനാകും.</translation>
 <translation id="3393554941209044235">Chrome ഡോക്യുമെന്റ് അനാലിസിസ്</translation>
 <translation id="3393582007140394275">സ്ക്രീൻ കാസ്റ്റ് ചെയ്യാനാകുന്നില്ല.</translation>
+<translation id="3394072120086516913">PC വയേർഡ് ആണ്, കാസ്റ്റിംഗ് ഉപകരണം വൈഫൈയിലാണ്</translation>
 <translation id="3394850431319394743">പരിരക്ഷിത ഉള്ളടക്കം പ്ലേ ചെയ്യുന്നതിന് ഐഡന്റിഫയറുകൾ ഉപയോഗിക്കാൻ അനുവദിച്ചിരിക്കുന്നു</translation>
 <translation id="3396442984945202128">ഇത് നിങ്ങൾ തന്നെയാണെന്ന് പരിശോധിച്ചുറപ്പിക്കൂ</translation>
 <translation id="3396800784455899911">"അംഗീകരിച്ച് തുടരുക" ബട്ടൺ ക്ലിക്ക് ചെയ്യുക വഴി, ഈ Google സേവനങ്ങൾക്കായി മുകളിൽ വിവരിച്ചിട്ടുള്ള പ്രോസസിംഗിന് നിങ്ങൾ സമ്മതിക്കുകയാണ്.</translation>
@@ -4488,6 +4498,7 @@
 <translation id="4314561087119792062">പുതിയ ആക്‌സസ് പോയിന്റിന്റെ പേര് ചേർക്കുക</translation>
 <translation id="4314815835985389558">സമന്വയിപ്പിക്കൽ മാനേജ് ചെയ്യുക</translation>
 <translation id="4316850752623536204">ഡെവലപ്പർ വെബ്‌സൈറ്റ്</translation>
+<translation id="43176328751044557">{NUM_SITES,plural, =1{ഒരു സൈറ്റിന്റെ അനുമതികൾ നീക്കം ചെയ്തു}other{{NUM_SITES} സൈറ്റുകളുടെ അനുമതികൾ നീക്കം ചെയ്തു}}</translation>
 <translation id="4317733381297736564">ആപ്പ് വഴി വാങ്ങൽ</translation>
 <translation id="4317820549299924617">പരിശോധിച്ചുറപ്പിക്കാനായില്ല</translation>
 <translation id="4319441675152393296"><ph name="HOST" /> വായിക്കാനും മാറ്റാനും ഈ വിപുലീകരണത്തിന്റെ ഐക്കണിൽ ക്ലിക്ക് ചെയ്യുക</translation>
@@ -4804,6 +4815,7 @@
 <translation id="4559617833001311418">നിങ്ങളുടെ ചലന സെൻസർ അല്ലെങ്കിൽ വെളിച്ച സെൻസർ ഈ സൈറ്റ് ആക്‌സസ് ചെയ്യുന്നു.</translation>
 <translation id="4560728518401799797"><ph name="FOLDER_TITLE" /> ബുക്ക്‌മാർക്കിനുള്ള കൂടുതൽ ഓപ്‌ഷനുകൾ</translation>
 <translation id="4561893854334016293">അടുത്തിടെ അനുമതികളൊന്നും മാറ്റിയിട്ടില്ല</translation>
+<translation id="4562091353415772246">അപ്‌ഡേറ്റുകളും ആപ്പുകളും ഇൻസ്റ്റാൾ ചെയ്യുക. തുടരുന്നതിലൂടെ, ഈ ഉപകരണം Google-ൽ നിന്നും നിങ്ങളുടെ സേവനദാതാവിൽ നിന്നും ഉപകരണ നിർമ്മാതാവിൽ നിന്നും, ഒരുപക്ഷേ സെല്ലുലാർ ഡാറ്റ ഉപയോഗിച്ച്, അപ്‌ഡേറ്റുകളും ആപ്പുകളും സ്വമേധയാ ഡൗൺലോഡ് ചെയ്‌ത് ഇൻസ്‌റ്റാൾ ചെയ്യുന്നത് നിങ്ങൾ അംഗീകരിക്കുന്നു. ഈ ആപ്പുകളിൽ ചിലത് 'ആപ്പ് വഴി വാങ്ങൽ' സൗകര്യം നൽകിയേക്കാം. <ph name="BEGIN_LINK1" />പ്ലേ സ്റ്റോർ വഴി സ്വയം ഇൻസ്റ്റലേഷൻ നടത്തുന്നതിനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK1_END" />കൂടുതലറിയുക<ph name="END_LINK1" /></translation>
 <translation id="4562155214028662640">വിരലടയാളം ചേർക്കുക</translation>
 <translation id="4562155266774382038">നിർദ്ദേശം ഡിസ്‌മിസ് ചെയ്യുക</translation>
 <translation id="4563210852471260509">പ്രാരംഭ ഇന്‍‌പുട്ട് ഭാഷ ചൈനീസ് ആണ്</translation>
@@ -5739,6 +5751,8 @@
 <translation id="5288106344236929384"><ph name="DOMAIN" /> എന്നതിലെ <ph name="USERNAME" /> എന്നതിനുള്ള കൂടുതൽ പ്രവർത്തനങ്ങളും പാസ്കീ ഓപ്ഷനുകളും</translation>
 <translation id="5288678174502918605">അടച്ച ടാബ് വീ&amp;ണ്ടും തുറക്കുക</translation>
 <translation id="52895863590846877"><ph name="LANGUAGE" /> ഭാഷയിൽ പേജ് ലഭ്യമല്ല</translation>
+<translation id="5290020561438336792">PC-യും കാസ്റ്റിംഗ് ഉപകരണവും വ്യത്യസ്ത വൈഫൈ നെറ്റ്‌വർക്കുകളിലാണുള്ളത് (ഉദാ. 2.4GHz
+    vs. 5GHz)</translation>
 <translation id="52912272896845572">സ്വകാര്യ കീ ഫയല്‍‌ അസാധുവാണ്.</translation>
 <translation id="5291739252352359682">Chrome ബ്രൗസറിൽ മീഡിയയ്‌ക്ക് അടിക്കുറിപ്പുകൾ സ്വയമേവ സൃഷ്‌ടിക്കുന്നു (നിലവിൽ ഇംഗ്ലീഷിൽ ലഭ്യം) ഓഡിയോയും അടിക്കുറിപ്പുകളും ലോക്കലായി പ്രോസസ് ചെയ്യുന്നു, അവ ഒരിക്കലും നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പുറത്ത് പോകില്ല.</translation>
 <translation id="529175790091471945">ഈ ഉപകരണം ഫോർമാറ്റ് ചെയ്യുക</translation>
@@ -6222,9 +6236,12 @@
 <translation id="5646558797914161501">ബിസിനസ്‌മാൻ</translation>
 <translation id="5648021990716966815">Mic jack</translation>
 <translation id="5648166631817621825">കഴിഞ്ഞ 7 ദിവസം</translation>
+<translation id="5649182603113127372">ലൊക്കേഷൻ ഉപയോഗിക്കുക. നിങ്ങളുടെ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ഉപയോഗിക്കാൻ, ലൊക്കേഷൻ അനുമതിയുള്ള ChromeOS, Android ആപ്പുകൾ, വെബ്‌സൈറ്റുകൾ, സേവനങ്ങൾ എന്നിവയെ അനുവദിക്കുക. ലൊക്കേഷൻ കൃത്യതയും ലൊക്കേഷൻ അധിഷ്‌ഠിത സേവനങ്ങളും മെച്ചപ്പെടുത്താൻ, Google കാലാകാലങ്ങളിൽ ലൊക്കേഷൻ ഡാറ്റ ശേഖരിക്കുകയും നിങ്ങളുടെ വ്യക്തിവിവരങ്ങൾ എടുത്തുനീക്കി അവ ഉപയോഗിക്കുകയും ചെയ്‌തേക്കാം. <ph name="BEGIN_LINK1" />ലൊക്കേഷനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK1_END" />കൂടുതലറിയുക<ph name="END_LINK1" /></translation>
+<translation id="5650537073531199882">ഫോം പൂരിപ്പിക്കുക</translation>
 <translation id="5651308944918885595">സമീപമുള്ള പങ്കിടൽ കണ്ടെത്തൽ ക്ഷമത</translation>
 <translation id="5653154844073528838">നിങ്ങൾക്ക് സംരക്ഷിച്ചിരിക്കുന്ന <ph name="PRINTER_COUNT" /> പ്രിന്ററുകളുണ്ട്.</translation>
 <translation id="5654669866168491665">മൂന്നാം കക്ഷി കുക്കികൾ ബ്ലോക്ക് ചെയ്താൽ പ്രവർത്തിക്കാനിടയില്ലാത്ത സൈറ്റുകളെക്കുറിച്ച് കൂടുതലറിയുക</translation>
+<translation id="5654848283274615843">{NUM_SITES,plural, =1{നിങ്ങളുടെ സ്വകാര്യത പരിരക്ഷിക്കാൻ, ഒരു സൈറ്റിൽ നിന്ന് അനുമതികൾ നീക്കം ചെയ്തു}other{നിങ്ങളുടെ സ്വകാര്യത പരിരക്ഷിക്കാൻ, ചില സൈറ്റുകളിൽ നിന്ന് അനുമതികൾ നീക്കം ചെയ്തു}}</translation>
 <translation id="565515993087783098">ഈ നെറ്റ്‌വർക്ക് മറക്കുന്നതിലൂടെ നിങ്ങൾ പാസ്‌പോയിന്റ് സബ്‌സ്‌ക്രിപ്‌ഷനും അതിന്റെ അനുബന്ധ നെറ്റ്‌വർക്കുകളും നീക്കം ചെയ്യും.</translation>
 <translation id="5655296450510165335">ഉപകരണ എൻറോൾമെന്റ്</translation>
 <translation id="5655823808357523308">നിങ്ങളുടെ സ്‌ക്രീനിൽ നിറങ്ങൾ പ്രദർശിപ്പിക്കുന്ന രീതി അഡ്‌ജ‌സ്റ്റ് ചെയ്യുക</translation>
@@ -6338,6 +6355,7 @@
 <translation id="5746169159649715125">PDF ആയി സംരക്ഷിക്കുക</translation>
 <translation id="5747785204778348146">ഡെവലപ്പർ - അസ്ഥിരം</translation>
 <translation id="5747809636523347288">ഒട്ടിച്ച്, <ph name="URL" /> എന്നതിലേക്ക് പോവുക</translation>
+<translation id="5749214722697335450">ഉപയോഗവും പ്രശ്‌നനിർണ്ണയവുമായി ബന്ധപ്പെട്ട ഡാറ്റ അയയ്ക്കുക. പ്രശ്‌നനിർണ്ണയം, ഉപകരണം, ആപ്പ് ഉപയോഗം എന്നിവയുമായി ബന്ധപ്പെട്ട ഡാറ്റ സ്വയമേവ Google-ന് അയച്ച്, നിങ്ങളുടെ Android അനുഭവം മെച്ചപ്പെടുത്താൻ സഹായിക്കുക. സിസ്‌റ്റം, ആപ്പ് സ്ഥിരത, മറ്റ് മെച്ചപ്പെടുത്തലുകൾ എന്നിവയ്ക്ക് ഇത് സഹായിക്കും. ചില സംഗ്രഹ ഡാറ്റ, Google ആപ്പുകളെയും Android ഡെവലപ്പർമാരെപ്പോലുള്ള പങ്കാളികളെയും സഹായിക്കുകയും ചെയ്യും. നിങ്ങളുടെ അധിക വെബ്, ആപ്പ് ആക്റ്റിവിറ്റി ക്രമീകരണം ഓണാക്കിയിട്ടുണ്ടെങ്കിൽ, ഈ ഡാറ്റ നിങ്ങളുടെ Google അക്കൗണ്ടിൽ സംരക്ഷിക്കപ്പെട്ടേക്കാം. <ph name="BEGIN_LINK1" />മെട്രിക്സിനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK1_END" />കൂടുതലറിയുക<ph name="END_LINK1" /></translation>
 <translation id="5750288053043553775">0</translation>
 <translation id="5751345516399502412">ടെതറിംഗ് റെഡിനസ് പരിശോധിക്കുക</translation>
 <translation id="5753570386948603678">ചരിത്രത്തിൽ നിന്ന് ഇല്ലാതാക്കുക</translation>
@@ -6373,6 +6391,7 @@
     ഇത് ഓഫാക്കുന്നത്, Android-ൽ ലൊക്കേഷനുവേണ്ടി IP വിലാസം മാത്രം ഉപയോഗിക്കുന്നതിന് ഇടയാക്കും. Maps പോലുള്ള ആപ്പുകൾ ഉപയോഗിക്കുന്ന ലൊക്കേഷന്റെ കൃത്യതയെ ഇത് ബാധിച്ചേക്കാം.</translation>
 <translation id="5778491106820461378">സൈൻ ഇൻ ചെയ്‌തിരിക്കുന്ന Google അക്കൗണ്ടുകൾ നിങ്ങൾക്ക് <ph name="LINK_BEGIN" />ക്രമീകരണത്തിൽ<ph name="LINK_END" /> മാനേജ് ചെയ്യാം. വെബ്‌സൈറ്റുകൾക്കും ആപ്പുകൾക്കും നിങ്ങൾ നൽകിയ അനുമതികൾ എല്ലാ അക്കൗണ്ടുകൾക്കും ബാധകമായേക്കാം. സൈറ്റുകളോ ആപ്പുകളോ നിങ്ങളുടെ അക്കൗണ്ടിലെ വിവരങ്ങൾ ആക്‌സസ് ചെയ്യണമെന്നില്ലെങ്കിൽ, നിങ്ങൾക്ക് <ph name="DEVICE_TYPE" /> ഉപകരണത്തിൽ അതിഥിയായി സൈൻ ചെയ്യാം.</translation>
 <translation id="5780011244986845107">നിങ്ങൾ തിരഞ്ഞെടുത്ത ഫോൾഡറിൽ സൂക്ഷ്‌മമായി കൈകാര്യം ചെയ്യേണ്ട ഫയലുകൾ അടങ്ങിയിരിക്കുന്നു. ഈ ഫോൾഡറിലേക്ക് "<ph name="APP_NAME" />" എന്നതിന് റീഡ് ചെയ്യാനുള്ള ശാശ്വതമായ ആക്‌സസ് അനുവദിക്കണമെന്ന് ഉറപ്പാണോ?</translation>
+<translation id="5780940414249100901">ഉപയോഗവും പ്രശ്‌നനിർണ്ണയവുമായി ബന്ധപ്പെട്ട ഡാറ്റ അയയ്ക്കുക. പ്രശ്‌നനിർണ്ണയം, ഉപകരണം, ആപ്പ് ഉപയോഗം എന്നിവയുമായി ബന്ധപ്പെട്ട ഡാറ്റ, ഈ ഉപകരണം നിലവിൽ സ്വയമേവ Google-ന് അയയ്ക്കുന്നുണ്ട്. നിങ്ങളുടെ കുട്ടിയെ തിരിച്ചറിയാൻ ഇത് ഉപയോഗിക്കില്ല, സിസ്‌റ്റം, ആപ്പ് സ്ഥിരത, മറ്റ് മെച്ചപ്പെടുത്തലുകൾ എന്നിവയ്ക്ക് സഹായിക്കുകയും ചെയ്യും. ചില സംഗ്രഹ ഡാറ്റ, Google ആപ്പുകളെയും Android ഡെവലപ്പർമാരെപ്പോലുള്ള പങ്കാളികളെയും സഹായിക്കുകയും ചെയ്യും. ഈ <ph name="BEGIN_LINK1" />ക്രമീകരണം<ph name="END_LINK1" /> നടപ്പിലാക്കിയിരിക്കുന്നത് ഉടമയാണ്. കുട്ടിയുടെ അധിക വെബ്, ആപ്പ് ആക്റ്റിവിറ്റി ക്രമീകരണം ഓണാക്കിയിട്ടുണ്ടെങ്കിൽ, ഈ ഡാറ്റ അവരുടെ Google Account-ൽ സംരക്ഷിക്കപ്പെട്ടേക്കാം.<ph name="BEGIN_LINK2" />മെട്രിക്സിനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK2_END" />കൂടുതലറിയുക<ph name="END_LINK2" /></translation>
 <translation id="5780973441651030252">പ്രോസസ്സ് മുൻഗണന</translation>
 <translation id="5781092003150880845"><ph name="ACCOUNT_FULL_NAME" /> ആയി സമന്വയിപ്പിക്കുക</translation>
 <translation id="5781865261247219930"><ph name="EXTENSION_NAME" /> എന്ന വിപുലീകരണത്തിലേക്ക് കമാൻഡുകൾ അയയ്ക്കുക</translation>
@@ -7066,6 +7085,7 @@
 <translation id="6294759976468837022">സ്വയമേവയുള്ള സ്‌കാൻ ചെയ്യൽ വേഗത</translation>
 <translation id="6295158916970320988">എല്ലാ സൈറ്റുകളും</translation>
 <translation id="6295855836753816081">സംരക്ഷിക്കുന്നു...</translation>
+<translation id="6297986260307280218">ഉപയോഗവും പ്രശ്‌നനിർണ്ണയവുമായി ബന്ധപ്പെട്ട ഡാറ്റ അയയ്ക്കുക. പ്രശ്‌നനിർണ്ണയം, ഉപകരണം, ആപ്പ് ഉപയോഗം എന്നിവയുമായി ബന്ധപ്പെട്ട ഡാറ്റ സ്വയമേവ Google-ന് അയച്ച്, നിങ്ങളുടെ Android അനുഭവം മെച്ചപ്പെടുത്താൻ സഹായിക്കുക. സിസ്‌റ്റം, ആപ്പ് സ്ഥിരത, മറ്റ് മെച്ചപ്പെടുത്തലുകൾ എന്നിവയ്ക്ക് ഇത് സഹായിക്കും. ചില സംഗ്രഹ ഡാറ്റ, Google ആപ്പുകളെയും Android ഡെവലപ്പർമാരെപ്പോലുള്ള പങ്കാളികളെയും സഹായിക്കുകയും ചെയ്യും. ഈ <ph name="BEGIN_LINK1" />ക്രമീകരണം<ph name="END_LINK1" /> നടപ്പിലാക്കിയിരിക്കുന്നത് ഉടമയാണ്. ഈ ഉപകരണത്തിലെ പ്രശ്‌നനിർണ്ണയവും ഉപയോഗവുമായി ബന്ധപ്പെട്ട ഡാറ്റ Google-ന് അയയ്ക്കാൻ ഉടമ തീരുമാനിച്ചേക്കാം. നിങ്ങളുടെ അധിക വെബ്, ആപ്പ് ആക്റ്റിവിറ്റി ക്രമീകരണം ഓണാക്കിയിട്ടുണ്ടെങ്കിൽ, ഈ ഡാറ്റ നിങ്ങളുടെ Google Account-ൽ സംരക്ഷിക്കപ്പെട്ടേക്കാം. <ph name="BEGIN_LINK2" />മെട്രിക്സിനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK2_END" />കൂടുതലറിയുക<ph name="END_LINK2" /></translation>
 <translation id="6298456705131259420">ഇവിടെ ലിസ്റ്റ് ചെയ്തിരിക്കുന്ന സൈറ്റുകളെ ബാധിക്കുന്നു. ഒരു ഡൊമെയ്ൻ നാമത്തിന് മുമ്പ് “[*.]” എന്ന് ചേർക്കുന്നത് മുഴുവൻ ഡൊമെയ്നിനുമായുള്ള ഒരു ഒഴിവാക്കൽ സൃഷ്ടിക്കുന്നു. ഉദാഹരണത്തിന്, "[*.]google.com" ചേർക്കുന്നത് അർത്ഥമാക്കുന്നത്, അത് google.com-ന്റെ ഭാഗമായതിനാൽ, mail.google.com എന്നതിനായും മൂന്നാം കക്ഷി കുക്കികൾ സജീവമാകും എന്നാണ്.</translation>
 <translation id="6298962879096096191">Android ആപ്പുകൾ ഇൻസ്‌റ്റാൾ ചെയ്യാൻ Google Play ഉപയോഗിക്കുക</translation>
 <translation id="6300177430812514606">ഡാറ്റ അയയ്‌ക്കുന്നതോ സ്വീകരിക്കുന്നതോ പൂർത്തിയാക്കുന്നത് അനുവദിക്കുന്നില്ല</translation>
@@ -8106,6 +8126,7 @@
 <translation id="710047887584828070">ഈ ടാബിന്റെ ഉള്ളടക്കം പങ്കിടുന്നു</translation>
 <translation id="710224247908684995">ഒരു വിപുലീകരണം സുരക്ഷിത ബ്രൗസിംഗ് ഓഫാക്കി</translation>
 <translation id="7102832101143475489">അഭ്യർത്ഥന കാലഹരണപ്പെട്ടു</translation>
+<translation id="7103944802169726298">PC-യും കാസ്റ്റിംഗ് ഉപകരണവും ഒരേ വൈഫൈ നെറ്റ്‌വർക്കിലാണുള്ളത്</translation>
 <translation id="710640343305609397">നെറ്റ്‍വര്‍ക്ക് ക്രമീകരണം തുറക്കുക</translation>
 <translation id="7107609441453408294">എല്ലാ സ്‌പീക്കറുകളിലും ഒരേ ഓഡിയോ പ്ലേ ചെയ്യുക</translation>
 <translation id="7108338896283013870">മറയ്ക്കുക</translation>
@@ -10258,10 +10279,12 @@
 <translation id="8695139659682234808">സജ്ജീകരിച്ചതിനുശേഷം രക്ഷാകർതൃ നിയന്ത്രണങ്ങൾ ചേർക്കുക</translation>
 <translation id="8695825812785969222">&amp;സ്ഥാനം തുറക്കുക...</translation>
 <translation id="8698269656364382265">മുമ്പത്തെ സ്ക്രീനിലേക്ക് മടങ്ങാൻ ഇടതുവശത്ത് നിന്ന് സ്വൈപ്പ് ചെയ്യുക.</translation>
+<translation id="8698432579173128320">Google Drive-ലേക്ക് ബാക്കപ്പ് ചെയ്യുക. എളുപ്പത്തിൽ നിങ്ങളുടെ ഡാറ്റ പുനഃസ്ഥാപിക്കുകയോ ഏതുസമയത്തും ഉപകരണം മാറുകയോ ചെയ്യുക. ബാക്കപ്പിൽ ആപ്പ് ഡാറ്റയും ഉൾപ്പെടുന്നു. നിങ്ങളുടെ ബാക്കപ്പുകൾ Google-ലേക്ക് അപ്‌ലോഡ് ചെയ്‌ത്, Google Account പാസ്‌വേഡ് ഉപയോഗിച്ച് എൻക്രിപ്റ്റ് ചെയ്യും. <ph name="BEGIN_LINK1" />ബാക്കപ്പിനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK1_END" />കൂടുതലറിയുക<ph name="END_LINK1" /></translation>
 <translation id="869884720829132584">അപ്ലിക്കേഷനുക‌ള്‍‌ മെനു</translation>
 <translation id="869891660844655955">കാലഹരണപ്പെടല്‍‌ തീയതി</translation>
 <translation id="8699188901396699995"><ph name="PRINTER_NAME" /> എന്നതിനുള്ള PPD</translation>
 <translation id="8700066369485012242">ഈ സൈറ്റിൽ മൂന്നാം-കക്ഷി കുക്കികളെ അനുവദിച്ചത് എന്തുകൊണ്ടാണെന്ന് ഞങ്ങളോട് പറയൂ</translation>
+<translation id="8700087567921985940">ലൊക്കേഷൻ ഉപയോഗിക്കുക. ലൊക്കേഷൻ അനുമതിയുള്ള ആപ്പുകളെയും സേവനങ്ങളെയും നിങ്ങളുടെ ഉപകരണത്തിന്‍റെ ലൊക്കേഷൻ ഉപയോഗിക്കാൻ അനുവദിക്കുക. Google, ഇടയ്ക്കിടെ ലൊക്കേഷൻ ഡാറ്റ ശേഖരിക്കുകയും, ലൊക്കേഷൻ കൃത്യതയും ലൊക്കേഷൻ അധിഷ്ഠിത സേവനങ്ങളും മെച്ചപ്പെടുത്താൻ, വ്യക്തിപരമായ വിവരങ്ങൾ നീക്കി ഈ ഡാറ്റ ഉപയോഗിക്കുകയും ചെയ്തേക്കാം. <ph name="BEGIN_LINK1" />ലൊക്കേഷനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK1_END" />കൂടുതലറിയുക<ph name="END_LINK1" /></translation>
 <translation id="8700416429250425628">ലോഞ്ചർ + ബാക്ക്‌സ്പെയ്‌സ്</translation>
 <translation id="8702278591052316269">മറച്ചിരിക്കുന്ന, സംരക്ഷിച്ച ടാബ് ഗ്രൂപ്പുകൾ അടങ്ങുന്ന മെനു</translation>
 <translation id="8702825062053163569">നിങ്ങളുടെ <ph name="DEVICE_TYPE" /> ലോക്ക് ചെയ്തിരിക്കുന്നു.</translation>
@@ -10513,6 +10536,7 @@
 <translation id="8881020143150461183">വീണ്ടും ശ്രമിക്കുക. സാങ്കേതിക പിന്തുണയ്‌ക്ക്, <ph name="CARRIER_NAME" /> എന്നതുമായി ബന്ധപ്പെടുക.</translation>
 <translation id="888256071122006425">മൗസ്, ടച്ച്‌പാഡ് ക്രമീകരണം</translation>
 <translation id="8883273463630735858">ടച്ച്‌പാഡ് ആക്‌സിലറേഷൻ പ്രവർത്തനക്ഷമമാക്കുക</translation>
+<translation id="8883478023074930307">ഉപയോഗവും പ്രശ്‌നനിർണ്ണയവുമായി ബന്ധപ്പെട്ട ഡാറ്റ അയയ്ക്കുക. പ്രശ്‌നനിർണ്ണയം, ഉപകരണം, ആപ്പ് ഉപയോഗം എന്നിവയുമായി ബന്ധപ്പെട്ട ഡാറ്റ, ഈ ഉപകരണം നിലവിൽ സ്വയമേവ Google-ന് അയയ്ക്കുന്നുണ്ട്. സിസ്‌റ്റം, ആപ്പ് സ്ഥിരത, മറ്റ് മെച്ചപ്പെടുത്തലുകൾ എന്നിവയ്ക്ക് ഇത് സഹായിക്കും. ചില സംഗ്രഹ ഡാറ്റ, Google ആപ്പുകളെയും Android ഡെവലപ്പർമാരെപ്പോലുള്ള പങ്കാളികളെയും സഹായിക്കുകയും ചെയ്യും. ഈ <ph name="BEGIN_LINK1" />ക്രമീകരണം<ph name="END_LINK1" /> നടപ്പിലാക്കിയിരിക്കുന്നത് ഉടമയാണ്. നിങ്ങളുടെ അധിക വെബ്, ആപ്പ് ആക്റ്റിവിറ്റി ക്രമീകരണം ഓണാക്കിയിട്ടുണ്ടെങ്കിൽ, ഈ ഡാറ്റ നിങ്ങളുടെ Google അക്കൗണ്ടിൽ സംരക്ഷിക്കപ്പെട്ടേക്കാം.<ph name="BEGIN_LINK2" />മെട്രിക്സിനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK2_END" />കൂടുതലറിയുക<ph name="END_LINK2" /></translation>
 <translation id="8884023684057697730"><ph name="BEGIN_BOLD" />നിങ്ങളുടെ ഡാറ്റ എങ്ങനെ മാനേജ് ചെയ്യാം:<ph name="END_BOLD" /> നിങ്ങളുടെ സ്വകാര്യത പരിരക്ഷിക്കുന്നതിന് 4 ആഴ്‌ചയിലധികം പഴക്കമുള്ള സൈറ്റുകൾ ലിസ്റ്റിൽ നിന്ന് ഞങ്ങൾ സ്വയമേവ ഇല്ലാതാക്കുന്നു. നിങ്ങൾ വീണ്ടും സന്ദർശിക്കുന്ന ഒരു സൈറ്റ് ലിസ്റ്റിൽ വീണ്ടും ദൃശ്യമായേക്കാം. ഒരു സൈറ്റ് ഒരിക്കലും നിങ്ങളുടെ താൽപ്പര്യങ്ങൾ നിർവ്വചിക്കരുതെന്നാണ് നിങ്ങൾ ആഗ്രഹിക്കുന്നതെങ്കിൽ, നിങ്ങൾക്ക് ആ സൈറ്റ് നീക്കം ചെയ്യാം.</translation>
 <translation id="8884570509232205463">നിങ്ങളുടെ ഉപകരണം ഇപ്പോൾ <ph name="UNLOCK_TIME" />-ന് ലോക്ക് ആകുന്നു.</translation>
 <translation id="8885449336974696155"><ph name="BEGIN_LINK" />നിലവിലെ ക്രമീകരണം<ph name="END_LINK" /> റിപ്പോർട്ട് ചെയ്‌തുകൊണ്ട് ChromeOS മികച്ചതാക്കാൻ സഹായിക്കുക</translation>
@@ -10942,6 +10966,7 @@
 <translation id="919686179725692564">നിങ്ങളുടെ ആപ്പുകൾ ബാക്കപ്പ് ചെയ്യുന്നതിനെ കുറിച്ച് കൂടുതലറിയുക</translation>
 <translation id="9199503643457729322">സ്വകാര്യതാ ഗൈഡിൽ നിന്ന് പുറത്തേക്ക് നാവിഗേറ്റ് ചെയ്യാൻ ക്ലിക്ക് ചെയ്യുക.</translation>
 <translation id="9199695835892108985">എല്ലാ സീരിയൽ പോർട്ട് അനുമതികളും റീസെറ്റ് ചെയ്യണോ?</translation>
+<translation id="9199853905755292769">ഉപയോഗവും പ്രശ്‌നനിർണ്ണയവുമായി ബന്ധപ്പെട്ട ഡാറ്റ അയയ്ക്കുക. പ്രശ്‌നനിർണ്ണയം, ഉപകരണം, ആപ്പ് ഉപയോഗം എന്നിവയുമായി ബന്ധപ്പെട്ട ഡാറ്റ, ഈ ഉപകരണം നിലവിൽ സ്വയമേവ Google-ന് അയയ്ക്കുന്നുണ്ട്. നിങ്ങളുടെ കുട്ടിയെ തിരിച്ചറിയാൻ ഇത് ഉപയോഗിക്കില്ല, സിസ്‌റ്റം, ആപ്പ് സ്ഥിരത, മറ്റ് മെച്ചപ്പെടുത്തലുകൾ എന്നിവയ്ക്ക് സഹായിക്കുകയും ചെയ്യും. ചില സംഗ്രഹ ഡാറ്റ, Google ആപ്പുകളെയും Android ഡെവലപ്പർമാരെപ്പോലുള്ള പങ്കാളികളെയും സഹായിക്കുകയും ചെയ്യും. നിങ്ങളുടെ കുട്ടിയുടെ അധിക വെബ്, ആപ്പ് ആക്റ്റിവിറ്റി ക്രമീകരണം ഓണാക്കിയിട്ടുണ്ടെങ്കിൽ, ഈ ഡാറ്റ അവരുടെ Google Account-ൽ സംരക്ഷിക്കപ്പെട്ടേക്കാം. <ph name="BEGIN_LINK2" />മെട്രിക്സിനെ കുറിച്ച് കൂടുതലറിയുക<ph name="BEGIN_LINK2_END" />കൂടുതലറിയുക<ph name="END_LINK2" /></translation>
 <translation id="9200339982498053969"><ph name="ORIGIN" /> എന്നതിന് <ph name="FOLDERNAME" /> എന്ന ഫോൾഡറിലെ ഫയലുകൾ എഡിറ്റ് ചെയ്യാനാകും</translation>
 <translation id="920045321358709304"><ph name="SEARCH_ENGINE" /> തിരയുക</translation>
 <translation id="9201117361710210082">മുമ്പ് കണ്ടു</translation>
diff --git a/chrome/app/resources/google_chrome_strings_lo.xtb b/chrome/app/resources/google_chrome_strings_lo.xtb
index 5e5659d..ec91fff 100644
--- a/chrome/app/resources/google_chrome_strings_lo.xtb
+++ b/chrome/app/resources/google_chrome_strings_lo.xtb
@@ -127,6 +127,7 @@
 <translation id="2770231113462710648">ປ່ຽນບຣາວ​ເຊີມາດຕະຖານເປັນ:</translation>
 <translation id="2775140325783767197">Chrome ບໍ່ສາມາດກວດລະຫັດຜ່ານຂອງທ່ານໄດ້. ລອງກວດເບິ່ງການເຊື່ອມຕໍ່ອິນເຕີເນັດຂອງທ່ານ.</translation>
 <translation id="2799223571221894425">ເລີ່ມເປີດໃຊ້ໃໝ່</translation>
+<translation id="2825024317344269723">ເວັບໄຊທີ່ເປັນອັນຕະລາຍ. Chrome ໄດ້ລຶບສິດເຂົ້າເຖິງການແຈ້ງເຕືອນອອກແລ້ວ.</translation>
 <translation id="2846251086934905009">ຂໍ້ຜິດພາດໃນການຕິດຕັ້ງ: ຕົວຕິດຕັ້ງເຮັດວຽກໄດ້ບໍ່ສົມບູນ. ຍົກເລີກການຕິດຕັ້ງແລ້ວ.</translation>
 <translation id="2847461019998147611">ສະ​ແດງ Google Chrome ເປັນພາສານີ້</translation>
 <translation id="2853415089995957805">Chrome ໂຫຼດໜ້າທີ່ທ່ານອາດຈະເຂົ້າເບິ່ງໄວ້ລ່ວງໜ້າ, ເພື່ອໃຫ້ພວກມັນໂຫຼດໄວຂຶ້ນເມື່ອທ່ານເຂົ້າເບິ່ງ</translation>
@@ -188,6 +189,7 @@
 <translation id="3583751698304738917">ທ່ານເຂົ້າສູ່ລະບົບດ້ວຍ <ph name="USER_EMAIL_ADDRESS" /> ໃນໂປຣໄຟລ໌ Chrome ອື່ນຢູ່ກ່ອນແລ້ວ</translation>
 <translation id="3595784445906693824">ເຂົ້າສູ່ລະບົບ Chrome ໃນໂປຣໄຟລ໌ໃໝ່ບໍ?</translation>
 <translation id="3596080736082218006">{COUNT,plural, =0{ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານກຳນົດໃຫ້ທ່ານເປີດ Chrome ຄືນໃໝ່ເພື່ອນຳໃຊ້ການອັບເດດ}=1{ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານກຳນົດໃຫ້ທ່ານເປີດ Chrome ຄືນໃໝ່ເພື່ອນຳໃຊ້ການອັບເດດ. ໜ້າຈໍທີ່ບໍ່ເປີດເຜີຍຕົວຕົນຂອງທ່ານຈະບໍ່ເປີດຄືນໃໝ່.}other{ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານກຳນົດໃຫ້ທ່ານເປີດ Chrome ຄືນໃໝ່ເພື່ອນຳໃຊ້ການອັບເດດ. ໜ້າຈໍທີ່ບໍ່ເປີດເຜີຍຕົວຕົນຂອງທ່ານ # ລາຍການຈະບໍ່ເປີດຄືນໃໝ່.}}</translation>
+<translation id="3609788354808247807">ທ່ານບໍ່ໄດ້ເຂົ້າເບິ່ງເມື່ອບໍ່ດົນມານີ້. Chromium ໄດ້ລຶບສິດເຂົ້າເຖິງ <ph name="PERMISSION_1" />, <ph name="PERMISSION_2" /> ແລະ <ph name="PERMISSION_3" /> ອອກແລ້ວ</translation>
 <translation id="3622797965165704966">ດຽວນີ້ມັນງ່າຍທີ່ຈະໃຊ້ Chrome ກັບບັນຊີ Google ຂອງທ່ານ ແລະຢູ່ໃນຄອມພິວເຕີທີ່ແຊຣ໌ແລ້ວ.</translation>
 <translation id="3635073343384702370">Chrome ສາມາດກວດເບິ່ງລະຫັດຜ່ານຂອງທ່ານເມື່ອທ່ານບັນທຶກພວກມັນໄວ້</translation>
 <translation id="3667616615096815454">ບໍ່ສາມາດຕິດຕັ້ງໄດ້, ເຊີບເວີບໍ່ຮູ້ຈັກແອັບພລິເຄຊັນ.</translation>
@@ -337,6 +339,7 @@
 <translation id="5941711191222866238">ຫຍໍ້ລົງ</translation>
 <translation id="5941830788786076944">ເຮັດ Google Chrome ເປັນບຣາວເຊີມາດຕະຖານ</translation>
 <translation id="5947104538377036631">ທາງລັດ Chrome</translation>
+<translation id="5953954252731207958">ທ່ານບໍ່ໄດ້ເຂົ້າເບິ່ງເມື່ອບໍ່ດົນມານີ້. Chrome ໄດ້ລຶບສິດເຂົ້າເຖິງ <ph name="PERMISSION" /> ອອກແລ້ວ</translation>
 <translation id="6003112304606738118">ກຳລັງດາວໂຫຼດ... ຍັງເຫຼືອ <ph name="HOURS" /> ຊົ່ວໂມງ</translation>
 <translation id="6014316319780893079">ດ້ວຍ <ph name="BEGIN_LINK" />ເຄື່ອງມືຈາກ Chrome<ph name="END_LINK" />, ທ່ານຈະສາມາດທ່ອງເວັບໄດ້ຢ່າງປອດໄພ ແລະ ຄວບຄຸມສິ່ງຕ່າງໆໄດ້</translation>
 <translation id="6022659036123304283">ເຮັດໃຫ້ Chrome ເປັນຂອງທ່ານ</translation>
@@ -351,6 +354,7 @@
 <translation id="6169866489629082767"><ph name="PAGE_TITLE" /> - Google Chrome</translation>
 <translation id="6173637689840186878"><ph name="PAGE_TITLE" /> - Google Chrome Beta</translation>
 <translation id="6182736845697986886">ການຕິດຕັ້ງບໍ່ສຳເລັດເນື່ອງຈາກເຊີບເວີອັບເດດເກີດຂໍ້ຜິດພາດພາຍໃນ.</translation>
+<translation id="6200139057479872438">ທ່ານບໍ່ໄດ້ເຂົ້າເບິ່ງເມື່ອບໍ່ດົນມານີ້. Chrome ໄດ້ລຶບສິດເຂົ້າເຖິງ <ph name="PERMISSION_1" /> ແລະ <ph name="PERMISSION_2" /> ອອກແລ້ວ</translation>
 <translation id="6235018212288296708">ກົດລະບຽບຂາເຂົ້າສໍາລັບ Google Chrome ເພື່ອອະນຸຍາດໃຫ້ການຈາລະຈອນ mDNS.</translation>
 <translation id="624230925347970731">Chrome ຈະປິດໄວໆນີ້</translation>
 <translation id="6247557882553405851">ຕົວຈັດການລະຫັດຜ່ານ Google</translation>
@@ -448,6 +452,7 @@
 <translation id="7651907282515937834">ໂລໂກ້ Chrome Enterprise</translation>
 <translation id="76531479118467370">Chrome ບລັອກການດາວໂຫຼດນີ້ ເນື່ອງຈາກທ່ານປິດ Safe Browsing ແລະ ເຮັດໃຫ້ບໍ່ສາມາດຢັ້ງຢືນໄຟລ໌ໄດ້</translation>
 <translation id="7655455401911432608">ປະຫວັດການທ່ອງເວັບຂອງທ່ານ, ບັນທຶກຂອງເວັບໄຊທີ່ທ່ານເຂົ້າເບິ່ງໂດຍໃຊ້ Chrome ຢູ່ອຸປະກອນນີ້.</translation>
+<translation id="7661924425853052955">ທ່ານບໍ່ໄດ້ເຂົ້າເບິ່ງເມື່ອບໍ່ດົນມານີ້. Chrome ໄດ້ລຶບສິດເຂົ້າເຖິງ <ph name="PERMISSION_1" />, <ph name="PERMISSION_2" /> ແລະ ອີກ <ph name="COUNT" /> ລາຍການອອກແລ້ວ</translation>
 <translation id="7668816516367091728">ເຮັດສິ່ງຕ່າງໆ ເຊັ່ນ: ແປພາສາ, ຄິດໄລ່ ແລະ ອື່ນໆໄດ້ໂດຍກົງຈາກແຖບທີ່ຢູ່</translation>
 <translation id="769538538642757151">Chrome ຈະແຈ້ງໃຫ້ທ່ານຮູ້ຫາກມີສິ່ງໃດທີ່ທ່ານຕ້ອງກວດສອບ</translation>
 <translation id="7747138024166251722">ຕົວຕິດຕັ້ງບໍ່ສາມາດສ້າງໄດເຣັກຕໍຣີຊົ່ວຄາວໄດ້. ກະລຸນາກວດເບິ່ງບ່ອນ​ວ່າງດິສກ໌ ແລະການອະນຸຍາດ ເພື່ອຕິດຕັ້ງຊອບແວ.</translation>
diff --git a/chrome/app/resources/google_chrome_strings_ml.xtb b/chrome/app/resources/google_chrome_strings_ml.xtb
index 28dd020..31405902 100644
--- a/chrome/app/resources/google_chrome_strings_ml.xtb
+++ b/chrome/app/resources/google_chrome_strings_ml.xtb
@@ -127,6 +127,7 @@
 <translation id="2770231113462710648">ഡിഫോൾട്ട് ബ്രൗസര്‍‌ ഇനിപ്പറയുന്നതിലേക്ക് മാറ്റുക:</translation>
 <translation id="2775140325783767197">Chrome-ന് നിങ്ങളുടെ പാസ്‌വേഡുകൾ പരിശോധിക്കാനാവില്ല. നിങ്ങളുടെ ഇന്റർനെറ്റ് കണക്ഷൻ പരിശോധിക്കുക.</translation>
 <translation id="2799223571221894425">വീണ്ടും സമാരംഭിക്കുക</translation>
+<translation id="2825024317344269723">അപകടകരമായ സൈറ്റ്. Chrome, അറിയിപ്പുകൾ നീക്കം ചെയ്തു.</translation>
 <translation id="2846251086934905009">ഇൻസ്റ്റാൾ ചെയ്യുന്നതിൽ പിശക്: ഇൻസ്റ്റാളർ പൂർത്തിയായില്ല. ഇൻസ്റ്റാൾ ചെയ്യൽ റദ്ദാക്കി.</translation>
 <translation id="2847461019998147611">ഈ ഭാഷയില്‍‌ Google Chrome പ്രദര്‍‌ശിപ്പിക്കുക</translation>
 <translation id="2853415089995957805">നിങ്ങൾ സന്ദർശിക്കാനിടയുള്ള പേജുകൾ Chrome മുൻകൂട്ടി ലോഡ് ചെയ്യുന്നു, അതുവഴി നിങ്ങൾ ആ പേജുകൾ സന്ദർശിക്കുമ്പോൾ അവ അതിവേഗം ലോഡ് ചെയ്യും</translation>
@@ -188,6 +189,7 @@
 <translation id="3583751698304738917">നിങ്ങൾ മറ്റൊരു Chrome പ്രൊഫൈലിൽ <ph name="USER_EMAIL_ADDRESS" /> ആയി നിലവിൽ സൈൻ ഇൻ ചെയ്തിരിക്കുന്നു</translation>
 <translation id="3595784445906693824">പുതിയൊരു പ്രൊഫൈലിൽ Chrome-ലേക്ക് സൈൻ ഇൻ ചെയ്യണോ?</translation>
 <translation id="3596080736082218006">{COUNT,plural, =0{അപ്‌ഡേറ്റ് ബാധകമാക്കുന്നതിന് Chrome വീണ്ടും ആരംഭിക്കാൻ നിങ്ങളുടെ അഡ്‌മിൻ ആവശ്യപ്പെടുന്നു}=1{അപ്‌ഡേറ്റ് ബാധകമാക്കുന്നതിന് Chrome വീണ്ടും ആരംഭിക്കാൻ നിങ്ങളുടെ അഡ്‌മിൻ ആവശ്യപ്പെടുന്നു. നിങ്ങളുടെ അദൃശ്യ വിൻഡോ വീണ്ടും തുറക്കില്ല.}other{അപ്‌ഡേറ്റ് ബാധകമാക്കുന്നതിന് Chrome വീണ്ടും ആരംഭിക്കാൻ നിങ്ങളുടെ അഡ്‌മിൻ ആവശ്യപ്പെടുന്നു. നിങ്ങളുടെ # അദൃശ്യ വിൻഡോകൾ വീണ്ടും തുറക്കില്ല.}}</translation>
+<translation id="3609788354808247807">നിങ്ങൾ അടുത്തിടെ സന്ദർശിച്ചിട്ടില്ല. <ph name="PERMISSION_1" />, <ph name="PERMISSION_2" />, <ph name="PERMISSION_3" /> എന്നീ അനുമതികൾ Chrome നീക്കം ചെയ്തു</translation>
 <translation id="3622797965165704966">ഇപ്പോൾ നിങ്ങളുടെ Google Account ഉപയോഗിച്ച്, പങ്കിട്ട കമ്പ്യൂട്ടറുകളിൽ Chromium ഉപയോഗിക്കാൻ എളുപ്പമാണ്.</translation>
 <translation id="3635073343384702370">നിങ്ങളുടെ പാസ്‌വേഡുകൾ സംരക്ഷിക്കുകയാണങ്കിൽ, Chrome-ന് അവ പരിശോധിക്കാനാകും</translation>
 <translation id="3667616615096815454">ഇൻസ്റ്റാൾ ചെയ്യാനാകുന്നില്ല, സെർവറിന് ഈ ആപ്പ് പരിചിതമല്ല.</translation>
@@ -337,6 +339,7 @@
 <translation id="5941711191222866238">ചെറുതാക്കുക</translation>
 <translation id="5941830788786076944">Google Chrome-നെ ഡിഫോൾട്ട് ബ്രൗസർ ആക്കുക</translation>
 <translation id="5947104538377036631">Chrome കുറുക്കുവഴി</translation>
+<translation id="5953954252731207958">നിങ്ങൾ അടുത്തിടെ സന്ദർശിച്ചിട്ടില്ല. <ph name="PERMISSION" /> എന്ന അനുമതി Chrome നീക്കം ചെയ്തു</translation>
 <translation id="6003112304606738118">ഡൗൺലോഡ് ചെയ്യുന്നു... <ph name="HOURS" /> മണിക്കൂർ ശേഷിക്കുന്നു</translation>
 <translation id="6014316319780893079"><ph name="BEGIN_LINK" />Chrome-ൽ നിന്നുള്ള ടൂളുകൾ<ph name="END_LINK" /> ഉപയോഗിച്ച്, നിങ്ങൾക്ക് സുരക്ഷിതമായി ബ്രൗസ് ചെയ്യാനും നിയന്ത്രണത്തിൽ തുടരാനുമാകും</translation>
 <translation id="6022659036123304283">Chrome-നെ നിങ്ങളുടേതാക്കുക</translation>
@@ -351,6 +354,7 @@
 <translation id="6169866489629082767"><ph name="PAGE_TITLE" /> - Google Chrome</translation>
 <translation id="6173637689840186878"><ph name="PAGE_TITLE" /> - Google Chrome ബീറ്റ</translation>
 <translation id="6182736845697986886">അപ്‌ഡേറ്റ് സെർവർ ആന്തരിക പിശക് കാരണം ഇൻസ്റ്റാൾ ചെയ്യുന്നത് പരാജയപ്പെട്ടു.</translation>
+<translation id="6200139057479872438">നിങ്ങൾ അടുത്തിടെ സന്ദർശിച്ചിട്ടില്ല. <ph name="PERMISSION_1" />, <ph name="PERMISSION_2" /> എന്നീ അനുമതികൾ Chrome നീക്കം ചെയ്തു</translation>
 <translation id="6235018212288296708">mDNS ട്രാഫിക് അനുവദിക്കാൻ Google Chrome-നുള്ള ഇൻബൗണ്ട് റൂൾ.</translation>
 <translation id="624230925347970731">Chrome ഉടൻ അടയ്‌ക്കും</translation>
 <translation id="6247557882553405851">Google Password Manager</translation>
@@ -448,6 +452,7 @@
 <translation id="7651907282515937834">Chrome എന്റർപ്രൈസ് ലോഗോ</translation>
 <translation id="76531479118467370">നിങ്ങൾ സുരക്ഷിത ബ്രൗസിംഗ് ഓഫാക്കിയതിനാലും ഫയൽ പരിശോധിച്ചുറപ്പിക്കാൻ കഴിയാത്തതിനാലും Chrome ഈ ഡൗൺലോഡ് ബ്ലോക്ക് ചെയ്തു</translation>
 <translation id="7655455401911432608">ഈ ഉപകരണത്തിൽ Chrome ഉപയോഗിച്ച് നിങ്ങൾ സന്ദർശിച്ച സൈറ്റുകളുടെ റെക്കോർഡ് ആയ നിങ്ങളുടെ ബ്രൗസിംഗ് ചരിത്രം.</translation>
+<translation id="7661924425853052955">നിങ്ങൾ അടുത്തിടെ സന്ദർശിച്ചിട്ടില്ല. <ph name="PERMISSION_1" />, <ph name="PERMISSION_2" /> എന്നിവയും മറ്റ് <ph name="COUNT" /> അനുമതികളും Chrome നീക്കം ചെയ്തു</translation>
 <translation id="7668816516367091728">വിവർത്തനം ചെയ്യുന്നതും കണക്ക് കൂട്ടുന്നതും മറ്റും പോലുള്ള കാര്യങ്ങൾ വിലാസ ബാറിൽ നിന്ന് നേരിട്ട് ചെയ്യൂ</translation>
 <translation id="769538538642757151">നിങ്ങളുടെ അവലോകനം ആവശ്യമുള്ള എന്തെങ്കിലും ഉണ്ടെങ്കിൽ Chrome നിങ്ങളെ അറിയിക്കും</translation>
 <translation id="7747138024166251722">ഇൻസ്റ്റാളറിന് താൽക്കാലിക ഡയറക്റ്ററി സൃഷ്‌ടിക്കാനായില്ല. ശൂന്യമായ ഡിസ്‍ക് സ്പെയിസും സോഫ്റ്റ്‌വെയർ ഇൻസ്റ്റാൾ ചെയ്യാനുള്ള അനുമതിയും പരിശോധിക്കുക.</translation>
diff --git a/chrome/browser/ash/extensions/file_manager/event_router.cc b/chrome/browser/ash/extensions/file_manager/event_router.cc
index 684d765..4de8f3c 100644
--- a/chrome/browser/ash/extensions/file_manager/event_router.cc
+++ b/chrome/browser/ash/extensions/file_manager/event_router.cc
@@ -1580,4 +1580,12 @@
   OnFileManagerPrefsChanged();
 }
 
+bool EventRouter::AddCloudOpenTask(const storage::FileSystemURL& file_url) {
+  return office_tasks_->cloud_open_tasks.emplace(file_url.path()).second;
+}
+
+void EventRouter::RemoveCloudOpenTask(const storage::FileSystemURL& file_url) {
+  office_tasks_->cloud_open_tasks.erase(file_url.path());
+}
+
 }  // namespace file_manager
diff --git a/chrome/browser/ash/extensions/file_manager/event_router.h b/chrome/browser/ash/extensions/file_manager/event_router.h
index 7c3af8fe..0e24a96 100644
--- a/chrome/browser/ash/extensions/file_manager/event_router.h
+++ b/chrome/browser/ash/extensions/file_manager/event_router.h
@@ -32,6 +32,7 @@
 #include "chrome/browser/ash/guest_os/public/guest_os_mount_provider.h"
 #include "chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry.h"
 #include "chrome/browser/ash/policy/skyvault/observer.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
 #include "chrome/common/extensions/api/file_manager_private.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
 #include "chromeos/dbus/dlp/dlp_client.h"
@@ -218,6 +219,11 @@
   // policy::local_user_files::Observer:
   void OnLocalUserFilesPolicyChanged() override;
 
+  // Records that there's a `CloudOpenTask` for the `file_url`.
+  bool AddCloudOpenTask(const storage::FileSystemURL& file_url);
+  // Removes the record of a `CloudOpenTask` for the `file_url`.
+  void RemoveCloudOpenTask(const storage::FileSystemURL& file_url);
+
   // Use this method for unit tests to bypass checking if there are any SWA
   // windows.
   void ForceBroadcastingForTesting(bool enabled) {
diff --git a/chrome/browser/ash/extensions/file_manager/office_tasks.h b/chrome/browser/ash/extensions/file_manager/office_tasks.h
index 95f730c..2e73c5c 100644
--- a/chrome/browser/ash/extensions/file_manager/office_tasks.h
+++ b/chrome/browser/ash/extensions/file_manager/office_tasks.h
@@ -18,6 +18,9 @@
   OfficeTasks();
   ~OfficeTasks();
 
+  // Keeps track of active `CloudOpenTask`s for a file.
+  std::set<const base::FilePath> cloud_open_tasks;
+
   // Keeps track of IO tasks interacting with ODFS.
   std::map<io_task::IOTaskId,
            std::unique_ptr<ash::file_system_provider::ScopedUserInteraction>>
diff --git a/chrome/browser/ash/file_manager/file_tasks_browsertest.cc b/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
index 07d14e2..ce7337f0 100644
--- a/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
@@ -37,6 +37,7 @@
 #include "chrome/browser/ash/crosapi/crosapi_manager.h"
 #include "chrome/browser/ash/crosapi/test_controller_ash.h"
 #include "chrome/browser/ash/drive/file_system_util.h"
+#include "chrome/browser/ash/extensions/file_manager/event_router_factory.h"
 #include "chrome/browser/ash/file_manager/app_id.h"
 #include "chrome/browser/ash/file_manager/file_manager_browsertest_base.h"
 #include "chrome/browser/ash/file_manager/file_manager_test_util.h"
@@ -824,9 +825,10 @@
                         full_action_id);
 }
 
-const FileSystemURL CreateOfficeFileSourceURL(Profile* profile) {
+const FileSystemURL CreateOfficeFileSourceURL(Profile* profile,
+                                              std::string name = "text.docx") {
   base::FilePath file =
-      util::GetMyFilesFolderForProfile(profile).AppendASCII("text.docx");
+      util::GetMyFilesFolderForProfile(profile).AppendASCII(name);
   return ash::cloud_upload::FilePathToFileSystemURL(
       profile, file_manager::util::GetFileManagerFileSystemContext(profile),
       file);
@@ -1862,7 +1864,7 @@
     std::optional<ash::file_system_provider::ProvidedFileSystemInfo>
         odfs_file_system_info = ash::cloud_upload::GetODFSInfo(profile());
     EXPECT_TRUE(odfs_file_system_info.has_value());
-    return odfs_file_system_info->mount_path().Append(relative_test_path_1_);
+    return odfs_file_system_info->mount_path().Append(relative_test_path_2_);
   }
 
   // Filesystem path for Android OneDrive documents provider to the directory
@@ -2181,8 +2183,117 @@
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 
 // Test to check that a second setup dialog will not launch when one
-// is already being shown.
-IN_PROC_BROWSER_TEST_F(OneDriveTest, CannotShowSetupDialog) {
+// is already being shown for a different file.
+IN_PROC_BROWSER_TEST_F(OneDriveTest, CannotShowDifferentSetupDialog) {
+  // Doing this before SetUpTest creates a FakeWebAppPublisher which would
+  // intercept Files app launching.
+  LaunchFilesAppAndWait();
+
+  // Creates a fake ODFS with a test file.
+  SetUpTest();
+
+  // Set online status to avoid the office fallback dialog showing.
+  SetNetworkConnected(true);
+
+  const TaskDescriptor open_in_office_task = CreateOpenInOfficeTask();
+  std::vector<storage::FileSystemURL> file_urls1{odfs_docx_test_file_url_1_};
+  std::vector<storage::FileSystemURL> file_urls2{odfs_pptx_test_file_url_2_};
+
+  // Watch for dialog URL chrome://cloud-upload.
+  GURL expected_dialog_URL(chrome::kChromeUICloudUploadURL);
+  content::TestNavigationObserver navigation_observer_dialog(
+      expected_dialog_URL);
+  navigation_observer_dialog.StartWatchingNewWebContents();
+
+  // Launches the first setup dialog (file handler dialog). Let it
+  // hang waiting for a choice from the user.
+  base::test::TestFuture<TaskResult, std::string> executed_future;
+  ExecuteFileTask(profile(), open_in_office_task, file_urls1,
+                  executed_future.GetCallback());
+  ASSERT_EQ(executed_future.Get<0>(), TaskResult::kOpened);
+
+  // Wait for the first setup dialog to open.
+  navigation_observer_dialog.Wait();
+  ASSERT_TRUE(navigation_observer_dialog.last_navigation_succeeded());
+
+  // Fails to launch a second setup dialog for a different file.
+  base::test::TestFuture<TaskResult, std::string> failed_future;
+  ExecuteFileTask(profile(), open_in_office_task, file_urls2,
+                  failed_future.GetCallback());
+  ASSERT_EQ(failed_future.Get<0>(), TaskResult::kFailed);
+
+  // Both open file requests will log the CloudProvider metric.
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kOpenInitialCloudProviderMetric,
+      ash::cloud_upload::CloudProvider::kOneDrive, 2);
+  // Only the second open file request will complete and with a
+  // kCannotShowSetupDialog TaskResult.
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kOneDriveTaskResultMetricName,
+      ash::cloud_upload::OfficeTaskResult::kCannotShowSetupDialog, 1);
+}
+
+// Test to check that a second move confirmation dialog will not launch when one
+// is already being shown for a different file.
+IN_PROC_BROWSER_TEST_F(OneDriveTest, CannotShowDifferentMoveConfirmation) {
+  // Doing this before SetUpTest creates a FakeWebAppPublisher which would
+  // intercept Files app launching.
+  LaunchFilesAppAndWait();
+
+  // Creates a fake ODFS.
+  SetUpTest();
+
+  const TaskDescriptor open_in_office_task = CreateOpenInOfficeTask();
+  FileSystemURL file_outside_one_drive1 =
+      CreateOfficeFileSourceURL(profile(), "text.docx");
+  FileSystemURL file_outside_one_drive2 =
+      CreateOfficeFileSourceURL(profile(), "text2.docx");
+  std::vector<storage::FileSystemURL> file_urls1{file_outside_one_drive1};
+  std::vector<storage::FileSystemURL> file_urls2{file_outside_one_drive2};
+
+  // Disable the setup flow for office files.
+  SetWordFileHandlerToFilesSWA(profile(), kActionIdWebDriveOfficeWord);
+
+  // Set online status to avoid the office fallback dialog showing.
+  SetNetworkConnected(true);
+
+  // Watch for dialog URL chrome://cloud-upload.
+  GURL expected_dialog_URL(chrome::kChromeUICloudUploadURL);
+  content::TestNavigationObserver navigation_observer_dialog(
+      expected_dialog_URL);
+  navigation_observer_dialog.StartWatchingNewWebContents();
+
+  // Launches the first move confirmation dialog. Let it
+  // hang waiting for a choice from the user.
+  base::test::TestFuture<TaskResult, std::string> executed_future;
+  ExecuteFileTask(profile(), open_in_office_task, file_urls1,
+                  executed_future.GetCallback());
+  ASSERT_EQ(executed_future.Get<0>(), TaskResult::kOpened);
+
+  // Wait for the first move confirmation dialog to open.
+  navigation_observer_dialog.Wait();
+  ASSERT_TRUE(navigation_observer_dialog.last_navigation_succeeded());
+
+  // Fails to launch a second move confirmation dialog for a different file.
+  base::test::TestFuture<TaskResult, std::string> failed_future;
+  ExecuteFileTask(profile(), open_in_office_task, file_urls2,
+                  failed_future.GetCallback());
+  ASSERT_EQ(failed_future.Get<0>(), TaskResult::kFailed);
+
+  // Both requests will log the TransferRequired metric.
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kOneDriveTransferRequiredMetric,
+      ash::cloud_upload::OfficeFilesTransferRequired::kMove, 2);
+  // Only the second open file request will complete and with a
+  // kCannotShowMoveConfirmation TaskResult.
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kOneDriveTaskResultMetricName,
+      ash::cloud_upload::OfficeTaskResult::kCannotShowMoveConfirmation, 1);
+}
+
+// Test to check that a second setup dialog will not launch when one
+// is already being shown for the same file.
+IN_PROC_BROWSER_TEST_F(OneDriveTest, CannotShowDuplicateSetupDialog) {
   // Doing this before SetUpTest creates a FakeWebAppPublisher which would
   // intercept Files app launching.
   LaunchFilesAppAndWait();
@@ -2202,8 +2313,6 @@
       expected_dialog_URL);
   navigation_observer_dialog.StartWatchingNewWebContents();
 
-  web_app_publisher_->ClearPastLaunches();
-
   // Launches the first setup dialog (file handler dialog). Let it
   // hang waiting for a choice from the user.
   base::test::TestFuture<TaskResult, std::string> executed_future;
@@ -2215,7 +2324,7 @@
   navigation_observer_dialog.Wait();
   ASSERT_TRUE(navigation_observer_dialog.last_navigation_succeeded());
 
-  // Fails to launch a second setup dialog.
+  // Fails to launch a second setup dialog for the same file.
   base::test::TestFuture<TaskResult, std::string> failed_future;
   ExecuteFileTask(profile(), open_in_office_task, file_urls,
                   failed_future.GetCallback());
@@ -2226,15 +2335,15 @@
       ash::cloud_upload::kOpenInitialCloudProviderMetric,
       ash::cloud_upload::CloudProvider::kOneDrive, 2);
   // Only the second open file request will complete and with a
-  // kCannotShowSetupDialog TaskResult.
+  // kFileAlreadyBeingOpened TaskResult.
   histogram_.ExpectUniqueSample(
       ash::cloud_upload::kOneDriveTaskResultMetricName,
-      ash::cloud_upload::OfficeTaskResult::kCannotShowSetupDialog, 1);
+      ash::cloud_upload::OfficeTaskResult::kFileAlreadyBeingOpened, 1);
 }
 
 // Test to check that a second move confirmation dialog will not launch when one
-// is already being shown.
-IN_PROC_BROWSER_TEST_F(OneDriveTest, CannotShowMoveConfirmation) {
+// is already being shown for the same file.
+IN_PROC_BROWSER_TEST_F(OneDriveTest, CannotShowDuplicateMoveConfirmation) {
   // Doing this before SetUpTest creates a FakeWebAppPublisher which would
   // intercept Files app launching.
   LaunchFilesAppAndWait();
@@ -2275,20 +2384,20 @@
                   failed_future.GetCallback());
   ASSERT_EQ(failed_future.Get<0>(), TaskResult::kFailed);
 
-  // Both open file requests will log the TransferRequired metric.
+  // Only the first file request will log the TransferRequired metric.
   histogram_.ExpectUniqueSample(
       ash::cloud_upload::kOneDriveTransferRequiredMetric,
-      ash::cloud_upload::OfficeFilesTransferRequired::kMove, 2);
+      ash::cloud_upload::OfficeFilesTransferRequired::kMove, 1);
   // Only the second open file request will complete and with a
-  // kCannotShowMoveConfirmation TaskResult.
+  // kFileAlreadyBeingOpened TaskResult.
   histogram_.ExpectUniqueSample(
       ash::cloud_upload::kOneDriveTaskResultMetricName,
-      ash::cloud_upload::OfficeTaskResult::kCannotShowMoveConfirmation, 1);
+      ash::cloud_upload::OfficeTaskResult::kFileAlreadyBeingOpened, 1);
 }
 
 // Test that ExecuteFileTask() will open an ODFS office file with the Open In
-// Office task.
-IN_PROC_BROWSER_TEST_F(OneDriveTest, OpenFileFromODFS) {
+// Office task whilst an unrelated file is being opened.
+IN_PROC_BROWSER_TEST_F(OneDriveTest, OpenDifferentFileFromODFS) {
   // Creates a fake ODFS with a test file.
   SetUpTest();
 
@@ -2303,6 +2412,12 @@
 
   web_app_publisher_->ClearPastLaunches();
 
+  // Add a current CloudOpenTask for unrelated file.
+  auto* event_router =
+      file_manager::EventRouterFactory::GetForProfile(profile());
+  ASSERT_TRUE(event_router);
+  event_router->AddCloudOpenTask(odfs_pptx_test_file_url_2_);
+
   ExecuteFileTask(profile(), open_in_office_task, file_urls, base::DoNothing());
 
   auto launches = web_app_publisher_->GetLaunches();
@@ -2329,6 +2444,102 @@
       ash::cloud_upload::OfficeOneDriveOpenErrors::kSuccess, 1);
 }
 
+// Test that ExecuteFileTask() will open the same ODFS office file twice in a
+// row.
+IN_PROC_BROWSER_TEST_F(OneDriveTest, OpenSameFileFromODFSTwiceInARow) {
+  // Creates a fake ODFS with a test file.
+  SetUpTest();
+
+  const TaskDescriptor open_in_office_task = CreateOpenInOfficeTask();
+  std::vector<storage::FileSystemURL> file_urls{odfs_docx_test_file_url_1_};
+
+  // Disable the setup flow for office files.
+  SetWordFileHandlerToFilesSWA(profile(), kActionIdWebDriveOfficeWord);
+
+  // Set online status to avoid the office fallback dialog showing.
+  SetNetworkConnected(true);
+
+  web_app_publisher_->ClearPastLaunches();
+
+  // Open the file.
+  ExecuteFileTask(profile(), open_in_office_task, file_urls, base::DoNothing());
+
+  auto launches = web_app_publisher_->GetLaunches();
+  ASSERT_EQ(1u, launches.size());
+  CHECK_EQ(launches[0].app_id, web_app::kMicrosoft365AppId);
+  CHECK_EQ(launches[0].intent_url, test::kODFSSampleUrl);
+
+  // Open the file twice in a row. This should not be blocked since the opens
+  // are not overlapping.
+  ExecuteFileTask(profile(), open_in_office_task, file_urls, base::DoNothing());
+
+  launches = web_app_publisher_->GetLaunches();
+  ASSERT_EQ(2u, launches.size());
+  CHECK_EQ(launches[1].app_id, web_app::kMicrosoft365AppId);
+  CHECK_EQ(launches[1].intent_url, test::kODFSSampleUrl);
+
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kNumberOfFilesToOpenWithOneDriveMetric, 1, 2);
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kOpenInitialCloudProviderMetric,
+      ash::cloud_upload::CloudProvider::kOneDrive, 2);
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kOneDriveOpenSourceVolumeMetric,
+      ash::cloud_upload::OfficeFilesSourceVolume::kMicrosoftOneDrive, 2);
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kOneDriveTransferRequiredMetric,
+      ash::cloud_upload::OfficeFilesTransferRequired::kNotRequired, 2);
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kOneDriveTaskResultMetricName,
+      ash::cloud_upload::OfficeTaskResult::kOpened, 2);
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kOneDriveErrorMetricName,
+      ash::cloud_upload::OfficeOneDriveOpenErrors::kSuccess, 2);
+}
+
+// Test that ExecuteFileTask() will not open an ODFS office file when that file
+// is already being opened.
+IN_PROC_BROWSER_TEST_F(OneDriveTest,
+                       CannotOpenFileFromODFSWhenAlreadyBeingOpened) {
+  // Creates a fake ODFS with a test file.
+  SetUpTest();
+
+  const TaskDescriptor open_in_office_task = CreateOpenInOfficeTask();
+  std::vector<storage::FileSystemURL> file_urls{odfs_docx_test_file_url_1_};
+
+  // Disable the setup flow for office files.
+  SetWordFileHandlerToFilesSWA(profile(), kActionIdWebDriveOfficeWord);
+
+  // Set online status to avoid the office fallback dialog showing.
+  SetNetworkConnected(true);
+
+  web_app_publisher_->ClearPastLaunches();
+
+  // Add a current CloudOpenTask for the file.
+  auto* event_router =
+      file_manager::EventRouterFactory::GetForProfile(profile());
+  ASSERT_TRUE(event_router);
+  event_router->AddCloudOpenTask(odfs_docx_test_file_url_1_);
+
+  // Fail to execute a duplicate CloudOpenTask for the file.
+  base::test::TestFuture<TaskResult, std::string> failed_future;
+  ExecuteFileTask(profile(), open_in_office_task, file_urls,
+                  failed_future.GetCallback());
+  ASSERT_EQ(failed_future.Get<0>(), TaskResult::kFailed);
+
+  auto launches = web_app_publisher_->GetLaunches();
+  ASSERT_EQ(0u, launches.size());
+
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kNumberOfFilesToOpenWithOneDriveMetric, 1, 1);
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kOpenInitialCloudProviderMetric,
+      ash::cloud_upload::CloudProvider::kOneDrive, 1);
+  histogram_.ExpectUniqueSample(
+      ash::cloud_upload::kOneDriveTaskResultMetricName,
+      ash::cloud_upload::OfficeTaskResult::kFileAlreadyBeingOpened, 1);
+}
+
 // Test that ExecuteFileTask() will open ODFS office files with the Open In
 // Office task.
 // TODO(b/242685536) add support for multiple files and reenable this test.
diff --git a/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.cc b/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.cc
index dd73c45e..7dd58db 100644
--- a/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.cc
+++ b/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.cc
@@ -40,8 +40,6 @@
 namespace mahi {
 
 namespace {
-// The word count threshold for a distillable page.
-static constexpr int kWordCountThreshold = 50;
 
 #if DCHECK_IS_ON()
 // Save the contents to the `Download` directory. This function is used for
@@ -60,11 +58,8 @@
 #endif
 }  // namespace
 
-MahiContentExtractionDelegate::MahiContentExtractionDelegate(
-    base::RepeatingCallback<void(const base::UnguessableToken&, bool)>
-        distillable_check_callback)
-    : distillable_check_callback_(std::move(distillable_check_callback)),
-      io_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+MahiContentExtractionDelegate::MahiContentExtractionDelegate()
+    : io_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
            base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {
   // Do not bind to the services if mahi is not enabled.
@@ -146,46 +141,6 @@
                      web_content_state.url, std::move(callback)));
 }
 
-void MahiContentExtractionDelegate::CheckDistillablity(
-    const WebContentState& web_content_state,
-    const base::Time& start_time) {
-  // Early returns if the snapshot is not valid.
-  // TODO(b/318565573) consider adding some error states so that OS side have a
-  // better sense of the operations on the browser side.
-  if (web_content_state.snapshot.root_id == ui::kInvalidAXNodeID) {
-    return;
-  }
-
-  // Only rule based algorithm is used for triggering check.
-  mojom::ExtractionMethodsPtr extraction_methods =
-      mojom::ExtractionMethods::New(/*use_algorithm=*/true,
-                                    /*use_screen2x=*/false);
-
-  // Generates the extraction request.
-  mojom::ExtractionRequestPtr extraction_request =
-      mojom::ExtractionRequest::New(
-          /*ukm_source_id=*/web_content_state.ukm_source_id,
-          /*snapshot=*/web_content_state.snapshot,
-          /*extraction_methods=*/std::move(extraction_methods));
-
-  remote_content_extraction_service_->GetContentSize(
-      std::move(extraction_request),
-      base::BindOnce(&MahiContentExtractionDelegate::OnGetContentSize,
-                     weak_pointer_factory_.GetWeakPtr(),
-                     web_content_state.page_id, start_time));
-}
-
-void MahiContentExtractionDelegate::OnGetContentSize(
-    const base::UnguessableToken& page_id,
-    const base::Time& start_time,
-    mojom::ContentSizeResponsePtr response) {
-  base::UmaHistogramMicrosecondsTimes(
-      chromeos::mahi::kMahiContentExtractionTriggeringLatency,
-      base::Time::Now() - start_time);
-  distillable_check_callback_.Run(page_id,
-                                  response->word_count >= kWordCountThreshold);
-}
-
 void MahiContentExtractionDelegate::OnGetContent(
     const base::UnguessableToken& page_id,
     const base::UnguessableToken& client_id,
diff --git a/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.h b/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.h
index afcfa00..1acc586 100644
--- a/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.h
+++ b/chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.h
@@ -24,11 +24,11 @@
 
 // This is the delegate of the mahi content extraction service. It is
 // responsible for the service setup, binding and requests.
+// TODO(b:336438243): updates the mojom to reflects the removal of
+// CheckDistabillity().
 class MahiContentExtractionDelegate {
  public:
-  explicit MahiContentExtractionDelegate(
-      base::RepeatingCallback<void(const base::UnguessableToken&, bool)>
-          distillable_check_callback);
+  MahiContentExtractionDelegate();
   MahiContentExtractionDelegate(const MahiContentExtractionDelegate&) = delete;
   MahiContentExtractionDelegate& operator=(
       const MahiContentExtractionDelegate&) = delete;
@@ -46,12 +46,6 @@
                       const base::UnguessableToken& client_id,
                       GetContentCallback callback);
 
-  // Requests the content extraction service to check the page distillability
-  // based on the a11y update. `distillable_check_callback_` will be triggered
-  // when the check is finished.
-  void CheckDistillablity(const WebContentState& web_content_state,
-                          const base::Time& start_time);
-
  private:
   void OnGetContentSize(const base::UnguessableToken& page_id,
                         const base::Time& start_time,
@@ -73,11 +67,6 @@
   mojo::Remote<mojom::ContentExtractionService>
       remote_content_extraction_service_;
 
-  // This is the callback function to notifies the `MahiWebContentManager` with
-  // the distillability check result.
-  base::RepeatingCallback<void(const base::UnguessableToken&, bool)>
-      distillable_check_callback_;
-
   // This task runner is used to save the extracted content to disk. It meant to
   // be used for debugging purposes only, and should not be used in production.
   const scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
diff --git a/chrome/browser/chromeos/mahi/mahi_tab_helper.cc b/chrome/browser/chromeos/mahi/mahi_tab_helper.cc
index 3051e48b..152436f 100644
--- a/chrome/browser/chromeos/mahi/mahi_tab_helper.cc
+++ b/chrome/browser/chromeos/mahi/mahi_tab_helper.cc
@@ -52,6 +52,10 @@
   MahiWebContentsManager::Get()->OnFocusedPageLoadComplete(web_contents());
 }
 
+void MahiTabHelper::WebContentsDestroyed() {
+  MahiWebContentsManager::Get()->WebContentsDestroyed(web_contents());
+}
+
 WEB_CONTENTS_USER_DATA_KEY_IMPL(MahiTabHelper);
 
 }  // namespace mahi
diff --git a/chrome/browser/chromeos/mahi/mahi_tab_helper.h b/chrome/browser/chromeos/mahi/mahi_tab_helper.h
index d649a6b..ed678407 100644
--- a/chrome/browser/chromeos/mahi/mahi_tab_helper.h
+++ b/chrome/browser/chromeos/mahi/mahi_tab_helper.h
@@ -34,6 +34,7 @@
   void OnWebContentsLostFocus(
       content::RenderWidgetHost* render_widget_host) override;
   void DocumentOnLoadCompletedInPrimaryMainFrame() override;
+  void WebContentsDestroyed() override;
 
  private:
   friend class content::WebContentsUserData<MahiTabHelper>;
diff --git a/chrome/browser/chromeos/mahi/mahi_web_contents_manager.cc b/chrome/browser/chromeos/mahi/mahi_web_contents_manager.cc
index 1c9a2cb..151cfa4d 100644
--- a/chrome/browser/chromeos/mahi/mahi_web_contents_manager.cc
+++ b/chrome/browser/chromeos/mahi/mahi_web_contents_manager.cc
@@ -15,11 +15,14 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "base/unguessable_token.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/chromeos/mahi/mahi_browser_client_impl.h"
 #include "chrome/browser/chromeos/mahi/mahi_browser_util.h"
 #include "chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.h"
+#include "chrome/browser/content_extraction/inner_text.h"
 #include "chrome/browser/favicon/favicon_utils.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/components/mahi/public/cpp/mahi_manager.h"
@@ -41,9 +44,14 @@
 namespace mahi {
 
 namespace {
+
 MahiWebContentsManager* g_mahi_web_content_manager_for_testing = nullptr;
 using chromeos::mahi::ButtonType;
-}
+
+// The character count threshold for a distillable page.
+static constexpr int kCharCountThreshold = 300;
+
+}  // namespace
 
 // static
 MahiWebContentsManager* MahiWebContentsManager::Get() {
@@ -56,7 +64,9 @@
 
 MahiWebContentsManager::MahiWebContentsManager() = default;
 
-MahiWebContentsManager::~MahiWebContentsManager() = default;
+MahiWebContentsManager::~MahiWebContentsManager() {
+  focused_web_contents_ = nullptr;
+}
 
 void MahiWebContentsManager::Initialize() {
   client_ = std::make_unique<
@@ -64,84 +74,66 @@
                              base::BindRepeating(
                                  &MahiWebContentsManager::RequestContent,
                                  weak_pointer_factory_.GetWeakPtr()));
-  content_extraction_delegate_ = std::make_unique<
-      MahiContentExtractionDelegate>(/*distillable_check_callback=*/
-                                     base::BindRepeating(
-                                         &MahiWebContentsManager::
-                                             OnFinishDistillableCheck,
-                                         weak_pointer_factory_.GetWeakPtr()));
+  content_extraction_delegate_ =
+      std::make_unique<MahiContentExtractionDelegate>();
   is_initialized_ = true;
 }
 
 void MahiWebContentsManager::OnFocusedPageLoadComplete(
     content::WebContents* web_contents) {
-  if (!is_initialized_ ||
-      (!g_mahi_web_content_manager_for_testing &&
-       !chromeos::MahiManager::Get()->IsSupportedWithCorrectFeatureKey())) {
+  if (!is_initialized_) {
     return;
   }
+
+  if (ShouldSkip(web_contents)) {
+    ClearFocusedWebContentState();
+    return;
+  }
+
   base::Time start_time = base::Time::Now();
 
-  // Page info may not be properly updated yet if the user forwards/backwards
-  // the tab through cache. Thus, if focused page's URL does not change, we
-  // don't create a new `focused_web_content_state_` here, and instead rely on
-  // the callback of `RequestAXTreeSnapshot` to update if needed.
-  if (web_contents->GetLastCommittedURL() != focused_web_content_state_.url) {
-    // Creates a new focused web content state, and fires
-    // `OnFocusedPageChanged()`
-    // event immediately so that `MahiManager` knows the focused page has
-    // changed.
-    focused_web_content_state_ = WebContentState(
-        web_contents->GetLastCommittedURL(), web_contents->GetTitle());
-    focused_web_content_state_.favicon = GetFavicon(web_contents);
+  focused_web_contents_ = web_contents;
+  focused_web_content_state_ =
+      WebContentState(focused_web_contents_->GetLastCommittedURL(),
+                      focused_web_contents_->GetTitle());
+  focused_web_content_state_.favicon = GetFavicon(focused_web_contents_);
+  // Notifies `MahiManager` the focused page has changed.
+  client_->OnFocusedPageChanged(focused_web_content_state_);
 
-    // If the page is in the skip list, sets its distillability to false and
-    // notifies `MahiManager` immediately without requesting the snapshot.
-    if (ShouldSkip(web_contents)) {
-      focused_web_content_state_.is_distillable.emplace(false);
-      client_->OnFocusedPageChanged(focused_web_content_state_);
-      return;
-    }
-
-    // Notifies `MahiManger` the focused page has changed.
-    client_->OnFocusedPageChanged(focused_web_content_state_);
+  auto* rfh = web_contents->GetPrimaryMainFrame();
+  if (!rfh || !rfh->IsRenderFrameLive()) {
+    return;
   }
 
-  // Requests the a11y tree snapshot.
-  content::RenderFrameHost* render_frame_host =
-      web_contents->GetPrimaryMainFrame();
-  if (render_frame_host) {
-    focused_web_content_state_.ukm_source_id =
-        render_frame_host->GetPageUkmSourceId();
-  }
-  web_contents->RequestAXTreeSnapshot(
-      base::BindOnce(&MahiWebContentsManager::OnGetSnapshot,
+  content_extraction::GetInnerText(
+      *rfh, /*node_id=*/std::nullopt,
+      base::BindOnce(&MahiWebContentsManager::OnGetInnerText,
                      weak_pointer_factory_.GetWeakPtr(),
-                     focused_web_content_state_.page_id, web_contents,
-                     start_time),
-      ui::kAXModeWebContentsOnly,
-      /* max_nodes= */ 5000, /* timeout= */ {});
+                     focused_web_content_state_.page_id, start_time));
 }
 
 void MahiWebContentsManager::ClearFocusedWebContentState() {
+  focused_web_contents_ = nullptr;
   focused_web_content_state_ = WebContentState(/*url=*/GURL(), /*title=*/u"");
   if (!is_initialized_) {
     return;
   }
 
-  // Notifies `MahiManger` the focused page has changed.
+  // Notifies `MahiManager` the focused page has changed.
   client_->OnFocusedPageChanged(focused_web_content_state_);
 }
 
+void MahiWebContentsManager::WebContentsDestroyed(
+    content::WebContents* web_contents) {
+  if (focused_web_contents_ == web_contents) {
+    ClearFocusedWebContentState();
+  }
+}
+
 void MahiWebContentsManager::OnContextMenuClicked(
     int64_t display_id,
     ButtonType button_type,
     const std::u16string& question) {
-  // Updates requested web content state. Don't update if the button type is
-  // `kSettings`.
-  if (button_type != ButtonType::kSettings) {
-    FocusedPageGotRequest();
-  }
   // Forwards the UI request to `MahiBrowserDelegate`.
   client_->OnContextMenuClicked(display_id, button_type, question);
 
@@ -182,81 +174,60 @@
   g_mahi_web_content_manager_for_testing = nullptr;
 }
 
+void MahiWebContentsManager::OnGetInnerText(
+    const base::UnguessableToken& page_id,
+    const base::Time& start_time,
+    std::unique_ptr<content_extraction::InnerTextResult> result) {
+  if (focused_web_content_state_.page_id != page_id || !focused_web_contents_) {
+    // TODO(b:336438243): Add UMA to track this.
+    return;
+  }
+  base::UmaHistogramMicrosecondsTimes(
+      chromeos::mahi::kMahiContentExtractionTriggeringLatency,
+      base::Time::Now() - start_time);
+  focused_web_content_state_.url = focused_web_contents_->GetLastCommittedURL();
+  focused_web_content_state_.title = focused_web_contents_->GetTitle();
+  focused_web_content_state_.favicon = GetFavicon(focused_web_contents_);
+  bool distillable =
+      result ? result->inner_text.length() > kCharCountThreshold : false;
+  focused_web_content_state_.is_distillable.emplace(distillable);
+  // Notifies `MahiManager` the focused page has changed.
+  client_->OnFocusedPageChanged(focused_web_content_state_);
+}
+
 void MahiWebContentsManager::OnGetSnapshot(
     const base::UnguessableToken& page_id,
     content::WebContents* web_contents,
     const base::Time& start_time,
+    GetContentCallback callback,
     const ui::AXTreeUpdate& snapshot) {
-  // Updates states and checks the distillability of the snapshot.
-  if (page_id == focused_web_content_state_.page_id) {
-    // If the acquired url does not match the one within the snapshot, updates
-    // `focused_web_content_state_` to ensure they match.
-    if (focused_web_content_state_.url != GURL(snapshot.tree_data.url)) {
-      focused_web_content_state_ =
-          WebContentState(GURL(snapshot.tree_data.url),
-                          base::UTF8ToUTF16(snapshot.tree_data.title));
-      // Attempts to update the favicon if the url of the focused web contents
-      // have updated.
-      if (web_contents && web_contents->GetLastCommittedURL() ==
-                              focused_web_content_state_.url) {
-        focused_web_content_state_.favicon = GetFavicon(web_contents);
-      }
-    }
-    focused_web_content_state_.snapshot = snapshot;
-
-    // When debugging is enabled, directly extracts contents.
-#if DCHECK_IS_ON()
-    if (chromeos::features::IsMahiDebuggingEnabled()) {
-      content_extraction_delegate_->ExtractContent(
-          focused_web_content_state_, client_->client_id(), base::DoNothing());
-    }
-#endif
-    content_extraction_delegate_->CheckDistillablity(focused_web_content_state_,
-                                                     start_time);
-  } else if (page_id == requested_web_content_state_.page_id) {
-    requested_web_content_state_.snapshot = snapshot;
-    content_extraction_delegate_->CheckDistillablity(
-        requested_web_content_state_, start_time);
+  if (focused_web_content_state_.page_id != page_id) {
+    // TODO(b:336438243): Add UMA to track this.
+    std::move(callback).Run(nullptr);
+    return;
   }
-}
-
-void MahiWebContentsManager::OnFinishDistillableCheck(
-    const base::UnguessableToken& page_id,
-    bool is_distillable) {
-  // Updates states and notifies the page state update.
-  if (page_id == focused_web_content_state_.page_id) {
-    focused_web_content_state_.is_distillable.emplace(is_distillable);
-    client_->OnFocusedPageChanged(focused_web_content_state_);
-  } else if (page_id == requested_web_content_state_.page_id) {
-    requested_web_content_state_.is_distillable.emplace(is_distillable);
-    client_->OnFocusedPageChanged(requested_web_content_state_);
-  }
+  focused_web_content_state_.snapshot = snapshot;
+  content_extraction_delegate_->ExtractContent(
+      focused_web_content_state_, client_->client_id(), std::move(callback));
 }
 
 void MahiWebContentsManager::RequestContent(
     const base::UnguessableToken& page_id,
     GetContentCallback callback) {
-  if (page_id == focused_web_content_state_.page_id) {
-    // Updates requested web content state, if the focused state is requested.
-    FocusedPageGotRequest();
-    // As the requested web content state has been updated, sends the requested
-    // web content state instead.
-    content_extraction_delegate_->ExtractContent(requested_web_content_state_,
-                                                 client_->client_id(),
-                                                 std::move(callback));
-  } else if (page_id == requested_web_content_state_.page_id) {
-    content_extraction_delegate_->ExtractContent(requested_web_content_state_,
-                                                 client_->client_id(),
-                                                 std::move(callback));
-  } else {
-    // Early return if no matching `page_id` is found.
+  if (focused_web_content_state_.page_id != page_id || !focused_web_contents_) {
+    // TODO(b:336438243): Add UMA to track this.
     std::move(callback).Run(nullptr);
     return;
   }
-}
 
-void MahiWebContentsManager::FocusedPageGotRequest() {
-  requested_web_content_state_ = focused_web_content_state_;
+  base::Time start_time = base::Time::Now();
+  focused_web_contents_->RequestAXTreeSnapshot(
+      base::BindOnce(&MahiWebContentsManager::OnGetSnapshot,
+                     weak_pointer_factory_.GetWeakPtr(),
+                     focused_web_content_state_.page_id, focused_web_contents_,
+                     start_time, std::move(callback)),
+      ui::kAXModeWebContentsOnly,
+      /* max_nodes= */ 5000, /* timeout= */ {});
 }
 
 gfx::ImageSkia MahiWebContentsManager::GetFavicon(
diff --git a/chrome/browser/chromeos/mahi/mahi_web_contents_manager.h b/chrome/browser/chromeos/mahi/mahi_web_contents_manager.h
index dff20bf5..32b70091 100644
--- a/chrome/browser/chromeos/mahi/mahi_web_contents_manager.h
+++ b/chrome/browser/chromeos/mahi/mahi_web_contents_manager.h
@@ -14,6 +14,7 @@
 #include "chrome/browser/chromeos/mahi/mahi_browser_client_impl.h"
 #include "chrome/browser/chromeos/mahi/mahi_browser_util.h"
 #include "chrome/browser/chromeos/mahi/mahi_content_extraction_delegate.h"
+#include "chrome/browser/content_extraction/inner_text.h"
 #include "chromeos/crosapi/mojom/mahi.mojom-forward.h"
 #include "ui/accessibility/ax_tree_update.h"
 #include "ui/gfx/image/image_skia.h"
@@ -55,6 +56,10 @@
   // Clears the focused web content state, and notifies mahi manager.
   void ClearFocusedWebContentState();
 
+  // Clears the focused web content and its state if the focused content is
+  // destroyed.
+  void WebContentsDestroyed(content::WebContents* web_contents);
+
   // Called when the browser context menu has been clicked by the user.
   // `question` is used only if `ButtonType` is kQA.
   // Virtual so we can override in tests.
@@ -84,9 +89,15 @@
   MahiWebContentsManager();
   virtual ~MahiWebContentsManager();
 
+  void OnGetInnerText(
+      const base::UnguessableToken& page_id,
+      const base::Time& start_time,
+      std::unique_ptr<content_extraction::InnerTextResult> result);
+
   void OnGetSnapshot(const base::UnguessableToken& page_id,
                      content::WebContents* web_contents,
                      const base::Time& start_time,
+                     GetContentCallback callback,
                      const ui::AXTreeUpdate& snapshot);
 
   void OnFinishDistillableCheck(const base::UnguessableToken& page_id,
@@ -97,10 +108,6 @@
   void RequestContent(const base::UnguessableToken& page_id,
                       GetContentCallback callback);
 
-  // Should be called when user requests on the focused page. We should update
-  // the focused page state to the requested page state.
-  void FocusedPageGotRequest();
-
   // Gets the favicon from the given web contents. Returns an empty imageskia if
   // there is no valid one.
   // Virtual so we can override in tests.
@@ -118,8 +125,7 @@
 
   // The state of the web content which get focus in the browser.
   WebContentState focused_web_content_state_{/*url=*/GURL(), /*title=*/u""};
-  // The state of the web content which is requested by the user.
-  WebContentState requested_web_content_state_{/*url=*/GURL(), /*title=*/u""};
+  raw_ptr<content::WebContents> focused_web_contents_;
 
   base::WeakPtrFactory<MahiWebContentsManager> weak_pointer_factory_{this};
 };
diff --git a/chrome/browser/chromeos/mahi/mahi_web_contents_manager_browsertest.cc b/chrome/browser/chromeos/mahi/mahi_web_contents_manager_browsertest.cc
index e6c1c19..25f3126 100644
--- a/chrome/browser/chromeos/mahi/mahi_web_contents_manager_browsertest.cc
+++ b/chrome/browser/chromeos/mahi/mahi_web_contents_manager_browsertest.cc
@@ -183,13 +183,6 @@
   }
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
-  // Initially, the focused state and the requested state should be different.
-  EXPECT_NE(
-      fake_mahi_web_contents_manager_->focused_web_content_state().page_id,
-      fake_mahi_web_contents_manager_->requested_web_content_state().page_id);
-  base::UnguessableToken focused_page_id =
-      fake_mahi_web_contents_manager_->focused_web_content_state().page_id;
-
   base::RunLoop run_loop;
   // Expects that `MahiBrowserDelegate` should receive the context menu click
   // action.
@@ -208,14 +201,6 @@
                                                         kQuestion);
   run_loop.Run();
 
-  // After the context menu request, the requested state should be updated to
-  // the focused state and the focused state stays the same.
-  EXPECT_EQ(
-      focused_page_id,
-      fake_mahi_web_contents_manager_->requested_web_content_state().page_id);
-  EXPECT_EQ(
-      focused_page_id,
-      fake_mahi_web_contents_manager_->focused_web_content_state().page_id);
   EXPECT_EQ(GURL(),
             fake_mahi_web_contents_manager_->focused_web_content_state().url);
   EXPECT_EQ(u"",
@@ -233,10 +218,6 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
   base::HistogramTester histogram;
 
-  // Initially, the focused state and the requested state should be different.
-  EXPECT_NE(
-      fake_mahi_web_contents_manager_->focused_web_content_state().page_id,
-      fake_mahi_web_contents_manager_->requested_web_content_state().page_id);
   // Initially, the focused state's favicon is empty.
   EXPECT_TRUE(fake_mahi_web_contents_manager_->focused_web_content_state()
                   .favicon.isNull());
@@ -295,9 +276,6 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
   // Initially, the focused state and the requested state should be different.
-  EXPECT_NE(
-      fake_mahi_web_contents_manager_->focused_web_content_state().page_id,
-      fake_mahi_web_contents_manager_->requested_web_content_state().page_id);
   base::UnguessableToken focused_page_id =
       fake_mahi_web_contents_manager_->focused_web_content_state().page_id;
 
@@ -333,11 +311,6 @@
   CreateWebContent();
   run_loop.Run();
 
-  // After the content request, the requested state should be updated to the
-  // focused state and the focused state stays the same.
-  EXPECT_EQ(
-      focused_page_id,
-      fake_mahi_web_contents_manager_->requested_web_content_state().page_id);
   EXPECT_EQ(
       focused_page_id,
       fake_mahi_web_contents_manager_->focused_web_content_state().page_id);
diff --git a/chrome/browser/chromeos/mahi/test/fake_mahi_web_contents_manager.h b/chrome/browser/chromeos/mahi/test/fake_mahi_web_contents_manager.h
index 0157fd01..4146c97 100644
--- a/chrome/browser/chromeos/mahi/test/fake_mahi_web_contents_manager.h
+++ b/chrome/browser/chromeos/mahi/test/fake_mahi_web_contents_manager.h
@@ -38,10 +38,6 @@
     focused_web_content_state_.is_distillable.emplace(value);
   }
 
-  WebContentState requested_web_content_state() {
-    return requested_web_content_state_;
-  }
-
   void RequestContentFromPage(const base::UnguessableToken& page_id,
                               GetContentCallback callback);
 
diff --git a/chrome/browser/compose/chrome_compose_client.cc b/chrome/browser/compose/chrome_compose_client.cc
index b0acd79..8efdce1 100644
--- a/chrome/browser/compose/chrome_compose_client.cc
+++ b/chrome/browser/compose/chrome_compose_client.cc
@@ -129,8 +129,7 @@
       url::Origin::Create(GURL(chrome::kChromeUIUntrustedComposeUrl))) {
     debug_session_ = std::make_unique<ComposeSession>(
         &GetWebContents(), GetModelExecutor(), GetModelQualityLogsUploader(),
-        GetSessionId(), GetInnerTextProvider(), autofill::FieldRendererId(-1),
-        this);
+        GetSessionId(), GetInnerTextProvider(), autofill::FieldRendererId(-1));
     debug_session_->set_collect_inner_text(false);
     debug_session_->set_fre_complete(
         pref_service_->GetBoolean(prefs::kPrefHasCompletedComposeFRE));
@@ -329,7 +328,7 @@
     auto new_session = std::make_unique<ComposeSession>(
         &GetWebContents(), GetModelExecutor(), GetModelQualityLogsUploader(),
         GetSessionId(), GetInnerTextProvider(),
-        trigger_field.global_id().renderer_id, this, std::move(callback));
+        trigger_field.global_id().renderer_id, std::move(callback));
     current_session = new_session.get();
     sessions_.insert_or_assign(active_compose_ids_.value().first,
                                std::move(new_session));
@@ -548,7 +547,6 @@
 }
 
 void ChromeComposeClient::DisableProactiveNudge() {
-  nudge_tracker_.OnUserDisabledNudge(/*single_site_only=*/false);
   proactive_nudge_enabled_.SetValue(false);
 }
 
@@ -564,7 +562,6 @@
 }
 
 void ChromeComposeClient::AddSiteToNeverPromptList(const url::Origin& origin) {
-  nudge_tracker_.OnUserDisabledNudge(/*single_site_only=*/true);
   ScopedDictPrefUpdate update(pref_service_,
                               prefs::kProactiveNudgeDisabledSitesWithTime);
   update->Set(origin.Serialize(), base::TimeToValue(base::Time::Now()));
@@ -583,14 +580,6 @@
   return allow_context_menu;
 }
 
-void ChromeComposeClient::OnSessionComplete(
-    autofill::FieldRendererId field_renderer_id,
-    compose::ComposeSessionCloseReason close_reason,
-    const compose::ComposeSessionEvents& events) {
-  nudge_tracker_.ComposeSessionCompleted(field_renderer_id, close_reason,
-                                         events);
-}
-
 void ChromeComposeClient::OnAfterFocusOnFormField(
     autofill::AutofillManager& manager,
     autofill::FormGlobalId form,
@@ -673,8 +662,6 @@
   page_ukm_tracker_ = std::make_unique<compose::PageUkmTracker>(
       page.GetMainDocument().GetPageUkmSourceId());
 
-  nudge_tracker_.Clear();
-
   compose::ComposeTextUsageLogger::GetOrCreateForCurrentDocument(
       &page.GetMainDocument());
 }
diff --git a/chrome/browser/compose/chrome_compose_client.h b/chrome/browser/compose/chrome_compose_client.h
index e0fd554..4f0effc 100644
--- a/chrome/browser/compose/chrome_compose_client.h
+++ b/chrome/browser/compose/chrome_compose_client.h
@@ -50,7 +50,6 @@
       public autofill::AutofillManager::Observer,
       public compose::mojom::ComposeClientUntrustedPageHandler,
       public compose::ProactiveNudgeTracker::Delegate,
-      public ComposeSession::Observer,
       public InnerTextProvider {
  public:
   using EntryPoint = autofill::AutofillComposeDelegate::UiEntryPoint;
@@ -75,11 +74,6 @@
   void OpenProactiveNudgeSettings() override;
   void AddSiteToNeverPromptList(const url::Origin& origin) override;
 
-  // ComposeSession::Observer:
-  void OnSessionComplete(autofill::FieldRendererId field_renderer_id,
-                         compose::ComposeSessionCloseReason close_reason,
-                         const compose::ComposeSessionEvents& events) override;
-
   // autofill::AutofillManager::Observer:
   // Used to observe field focus changes so that the saved state notification
   // is only shown when an autofill suggestion will not be shown on another
diff --git a/chrome/browser/compose/chrome_compose_client_unittest.cc b/chrome/browser/compose/chrome_compose_client_unittest.cc
index 5be802d..6a335dc 100644
--- a/chrome/browser/compose/chrome_compose_client_unittest.cc
+++ b/chrome/browser/compose/chrome_compose_client_unittest.cc
@@ -9,7 +9,6 @@
 
 #include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
-#include "base/run_loop.h"
 #include "base/strings/utf_string_conversion_utils.h"
 #include "base/test/bind.h"
 #include "base/test/gmock_callback_support.h"
@@ -20,9 +19,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
-#include "base/time/time.h"
 #include "chrome/browser/compose/compose_enabling.h"
-#include "chrome/browser/segmentation_platform/segmentation_platform_service_factory.h"
 #include "chrome/common/compose/compose.mojom.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
@@ -41,8 +38,6 @@
 #include "components/optimization_guide/proto/features/compose.pb.h"
 #include "components/optimization_guide/proto/model_execution.pb.h"
 #include "components/optimization_guide/proto/model_quality_service.pb.h"
-#include "components/segmentation_platform/public/constants.h"
-#include "components/segmentation_platform/public/testing/mock_segmentation_platform_service.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "components/unified_consent/pref_names.h"
 #include "content/public/browser/navigation_entry.h"
@@ -66,14 +61,11 @@
     OptimizationGuideModelExecutionResultStreamingCallback;
 using optimization_guide::OptimizationGuideModelStreamingExecutionResult;
 using optimization_guide::StreamingResponse;
-using segmentation_platform::MockSegmentationPlatformService;
 
 namespace {
 
 const uint64_t kSessionIdHigh = 1234;
 const uint64_t kSessionIdLow = 5678;
-const segmentation_platform::TrainingRequestId kTrainingRequestId =
-    segmentation_platform::TrainingRequestId(456);
 constexpr char kTypeURL[] =
     "type.googleapis.com/optimization_guide.proto.ComposeResponse";
 
@@ -169,15 +161,6 @@
     scoped_compose_enabled_ = ComposeEnabling::ScopedEnableComposeForTesting();
     BrowserWithTestWindowTest::SetUp();
 
-    segmentation_platform::SegmentationPlatformServiceFactory::GetInstance()
-        ->SetTestingFactory(
-            GetProfile(),
-            base::BindLambdaForTesting([](content::BrowserContext* context) {
-              std::unique_ptr<KeyedService> result =
-                  std::make_unique<MockSegmentationPlatformService>();
-              return result;
-            }));
-
     scoped_feature_list_.InitWithFeatures(
         {compose::features::kEnableCompose,
          optimization_guide::features::kOptimizationGuideModelExecution},
@@ -228,25 +211,10 @@
                                                    LogAiDataRequest>(),
                               nullptr))));
             })));
-
-    ON_CALL(GetSegmentationPlatformService(),
-            GetClassificationResult(_, _, _, _))
-        .WillByDefault(testing::WithArg<3>(testing::Invoke(
-            [](segmentation_platform::ClassificationResultCallback callback) {
-              auto result = segmentation_platform::ClassificationResult(
-                  segmentation_platform::PredictionStatus::kSucceeded);
-              result.request_id = kTrainingRequestId;
-              result.ordered_labels = {
-                  segmentation_platform::kComposePrmotionLabelShow};
-              std::move(callback).Run(result);
-            })));
-
     test_timer_ = std::make_unique<base::ScopedMockElapsedTimersForTest>();
   }
 
   void TearDown() override {
-    // Clear default actions for safe teardown.
-    testing::Mock::VerifyAndClear(&GetSegmentationPlatformService());
     client_ = nullptr;
     scoped_feature_list_.Reset();
     ukm_recorder_.reset();
@@ -359,12 +327,6 @@
     field_data().set_selected_text(selection.substr(0, max_length));
   }
 
-  MockSegmentationPlatformService& GetSegmentationPlatformService() {
-    return *static_cast<MockSegmentationPlatformService*>(
-        segmentation_platform::SegmentationPlatformServiceFactory::
-            GetForProfile(GetProfile()));
-  }
-
  protected:
   optimization_guide::proto::ComposePageMetadata ComposePageMetadata() {
     optimization_guide::proto::ComposePageMetadata page_metadata;
@@ -774,55 +736,6 @@
               ukm::builders::Compose_PageEvents::kProactiveNudgeShownName, 0)));
 }
 
-TEST_F(ChromeComposeClientTest, TestProactiveNudgeEngagementIsRecorded) {
-  // Enable and trigger the proactive nudge.
-  compose::Config& config = compose::GetMutableConfigForTesting();
-  config.proactive_nudge_enabled = true;
-  config.proactive_nudge_show_probability = 1.0;
-  config.proactive_nudge_delay = base::Microseconds(1);
-  config.proactive_nudge_segmentation = true;
-
-  autofill::FormData form_data;
-  form_data.url = web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL();
-  form_data.fields = {autofill::test::CreateTestFormField(
-      "label0", "name0", "value0", autofill::FormControlType::kTextArea)};
-
-  autofill::FormFieldData selected_field_data = form_data.fields[0];
-  selected_field_data.set_origin(
-      web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
-  const autofill::AutofillSuggestionTriggerSource trigger_source =
-      autofill::AutofillSuggestionTriggerSource::kTextFieldDidChange;
-
-  while (!client().ShouldTriggerPopup(selected_field_data, trigger_source)) {
-    task_environment()->RunUntilIdle();
-  }
-
-  // Simulate clicking on the nudge to open compose.
-  ShowDialogAndBindMojoWithFieldData(
-      selected_field_data, base::NullCallback(),
-      autofill::AutofillComposeDelegate::UiEntryPoint::kAutofillPopup);
-
-  base::test::TestFuture<segmentation_platform::TrainingLabels> training_labels;
-  EXPECT_CALL(GetSegmentationPlatformService(),
-              CollectTrainingData(
-                  segmentation_platform::proto::SegmentId::
-                      OPTIMIZATION_TARGET_SEGMENTATION_COMPOSE_PROMOTION,
-                  kTrainingRequestId, _, _))
-      .Times(1)
-      .WillOnce(testing::WithArg<2>(testing::Invoke(
-          [&](auto labels) { training_labels.SetValue(labels); })));
-
-  client().CloseUI(compose::mojom::CloseReason::kInsertButton);
-
-  // Trigger session deletion and verify that the engagement is recorded.
-  NavigateAndCommitActiveTab(GURL("about:blank"));
-  EXPECT_EQ(training_labels.Get().output_metric,
-            std::make_pair("Compose.ProactiveNudge.DerivedEngagement",
-                           static_cast<base::HistogramBase::Sample>(
-                               compose::ProactiveNudgeDerivedEngagement::
-                                   kAcceptedComposeSuggestion)));
-}
-
 TEST_F(ChromeComposeClientTest, TestShouldTriggerProactiveNudgeDisabledUKM) {
   autofill::FormData form_data;
   form_data.url = web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL();
diff --git a/chrome/browser/compose/compose_session.cc b/chrome/browser/compose/compose_session.cc
index ea012aa7..29fdd416 100644
--- a/chrome/browser/compose/compose_session.cc
+++ b/chrome/browser/compose/compose_session.cc
@@ -202,7 +202,6 @@
     base::Token session_id,
     InnerTextProvider* inner_text,
     autofill::FieldRendererId node_id,
-    Observer* observer,
     ComposeCallback callback)
     : executor_(executor),
       handler_receiver_(this),
@@ -215,7 +214,6 @@
       close_reason_(compose::ComposeSessionCloseReason::kEndedImplicitly),
       final_status_(optimization_guide::proto::FinalStatus::STATUS_UNSPECIFIED),
       web_contents_(web_contents),
-      observer_(observer),
       collect_inner_text_(
           base::FeatureList::IsEnabled(compose::features::kComposeInnerText)),
       inner_text_caller_(inner_text),
@@ -263,10 +261,6 @@
   std::optional<compose::EvalLocation> eval_location =
       compose::GetEvalLocationFromEvents(session_events_);
 
-  if (observer_) {
-    observer_->OnSessionComplete(node_id_, close_reason_, session_events_);
-  }
-
   if (session_events_.fre_dialog_shown_count > 0 &&
       (!fre_complete_ || session_events_.fre_completed_in_session)) {
     compose::LogComposeFirstRunSessionCloseReason(fre_close_reason_);
diff --git a/chrome/browser/compose/compose_session.h b/chrome/browser/compose/compose_session.h
index 34b6ab28..3aab636 100644
--- a/chrome/browser/compose/compose_session.h
+++ b/chrome/browser/compose/compose_session.h
@@ -76,13 +76,6 @@
   // form field on which it was triggered.
   using ComposeCallback = base::OnceCallback<void(const std::u16string&)>;
 
-  class Observer {
-   public:
-    virtual void OnSessionComplete(
-        autofill::FieldRendererId node_id,
-        compose::ComposeSessionCloseReason close_reason,
-        const compose::ComposeSessionEvents& events) = 0;
-  };
   ComposeSession(
       content::WebContents* web_contents,
       optimization_guide::OptimizationGuideModelExecutor* executor,
@@ -90,7 +83,6 @@
       base::Token session_id,
       InnerTextProvider* inner_text,
       autofill::FieldRendererId node_id,
-      Observer* observer,
       ComposeCallback callback = base::NullCallback());
   ~ComposeSession() override;
 
@@ -343,8 +335,6 @@
   // `this`.
   raw_ptr<content::WebContents> web_contents_;
 
-  raw_ptr<Observer> observer_;
-
   // A callback to Autofill that triggers filling the field.
   ComposeCallback callback_;
 
diff --git a/chrome/browser/compose/proactive_nudge_tracker.cc b/chrome/browser/compose/proactive_nudge_tracker.cc
index 5ee3e82c..4467608 100644
--- a/chrome/browser/compose/proactive_nudge_tracker.cc
+++ b/chrome/browser/compose/proactive_nudge_tracker.cc
@@ -4,69 +4,11 @@
 
 #include "chrome/browser/compose/proactive_nudge_tracker.h"
 
-#include <memory>
-
-#include "base/functional/callback_helpers.h"
-#include "components/compose/core/browser/compose_metrics.h"
 #include "components/compose/core/browser/config.h"
 #include "components/segmentation_platform/public/constants.h"
 
 namespace compose {
 
-// Tracks user engagement as a result of the nudge.
-class ProactiveNudgeTracker::EngagementTracker {
- public:
-  EngagementTracker(
-      autofill::FieldRendererId field_renderer_id,
-      segmentation_platform::TrainingRequestId training_request_id,
-      ProactiveNudgeTracker* nudge_tracker)
-      : field_renderer_id_(field_renderer_id),
-        training_request_id_(training_request_id),
-        nudge_tracker_(nudge_tracker) {}
-  EngagementTracker(const EngagementTracker&) = delete;
-  EngagementTracker& operator=(const EngagementTracker&) = delete;
-
-  ~EngagementTracker() {
-    ReportIfFirst(ProactiveNudgeDerivedEngagement::kIgnored);
-  }
-
-  void ComposeSessionCompleted(ComposeSessionCloseReason session_close_reason,
-                               const compose::ComposeSessionEvents& events) {
-    if (events.inserted_results) {
-      ReportIfFirst(
-          ProactiveNudgeDerivedEngagement::kAcceptedComposeSuggestion);
-    } else if (events.compose_count > 0) {
-      ReportIfFirst(
-          ProactiveNudgeDerivedEngagement::kGeneratedComposeSuggestion);
-    } else {
-      ReportIfFirst(ProactiveNudgeDerivedEngagement::kOpenedComposeMinimalUse);
-    }
-  }
-
-  void UserDisabledNudge(bool single_site_only) {
-    if (single_site_only) {
-      ReportIfFirst(
-          ProactiveNudgeDerivedEngagement::kNudgeDisabledOnSingleSite);
-    } else {
-      ReportIfFirst(ProactiveNudgeDerivedEngagement::kNudgeDisabledOnAllSites);
-    }
-  }
-
- private:
-  void ReportIfFirst(ProactiveNudgeDerivedEngagement engagement) {
-    if (reported_) {
-      return;
-    }
-    nudge_tracker_->CollectTrainingData(training_request_id_, engagement);
-    reported_ = true;
-  }
-
-  bool reported_ = false;
-  autofill::FieldRendererId field_renderer_id_;
-  segmentation_platform::TrainingRequestId training_request_id_;
-  raw_ptr<ProactiveNudgeTracker> nudge_tracker_;
-};
-
 ProactiveNudgeTracker::State::State() = default;
 ProactiveNudgeTracker::State::~State() = default;
 
@@ -85,11 +27,7 @@
                         InitializationPolicy::kObservePreexistingManagers);
 }
 
-ProactiveNudgeTracker::~ProactiveNudgeTracker() {
-  // Destroy all engagement trackers first to ensure CollectTrainingData() is
-  // called before other parts of this class are destroyed.
-  engagement_trackers_.clear();
-}
+ProactiveNudgeTracker::~ProactiveNudgeTracker() = default;
 
 bool ProactiveNudgeTracker::ProactiveNudgeRequestedForFormField(
     const autofill::FormFieldData& field_to_track) {
@@ -123,6 +61,8 @@
         segmentation_platform::kComposePromotionKey, options, nullptr,
         base::BindOnce(&ProactiveNudgeTracker::GotClassificationResult,
                        weak_ptr_factory_.GetWeakPtr(), state_->AsWeakPtr()));
+  } else {
+    state_->segmentation_result = true;
   }
 
   base::TimeDelta delay = compose::GetComposeConfig().proactive_nudge_delay;
@@ -134,7 +74,7 @@
                         &ProactiveNudgeTracker::ShowTimerElapsed);
   }
 
-  if (ShouldShow(*state_)) {
+  if (state_->segmentation_result && state_->timer_complete) {
     // If the timer is 0-duration and no segmentation result is required, then
     // just transition to Shown state directly before returning true.
     state_->show_state = ShowState::kShown;
@@ -143,27 +83,10 @@
   return false;
 }
 
-bool ProactiveNudgeTracker::ShouldShow(const State& state) {
-  if (!state.timer_complete) {
-    return false;
-  }
-  if (!compose::GetComposeConfig().proactive_nudge_segmentation) {
-    return true;
-  }
-  return state.segmentation_result &&
-         state.segmentation_result->ordered_labels[0] ==
-             segmentation_platform::kComposePrmotionLabelShow;
-}
-
 void ProactiveNudgeTracker::FocusChangedInPage() {
   ResetState();
 }
 
-void ProactiveNudgeTracker::Clear() {
-  engagement_trackers_.clear();
-  ResetState();
-}
-
 void ProactiveNudgeTracker::OnAfterFocusOnFormField(
     autofill::AutofillManager& manager,
     autofill::FormGlobalId form,
@@ -204,20 +127,12 @@
 
 void ProactiveNudgeTracker::MaybeShowProactiveNudge() {
   DVLOG(2) << "ProactiveNudgeTracker: MaybeShowProactiveNudge ";
-  if (!state_ || !ShouldShow(*state_)) {
-    return;
+  if (state_ && state_->segmentation_result.value_or(false) &&
+      state_->timer_complete) {
+    // Transition to the SHOWN state.
+    delegate_->ShowProactiveNudge(state_->form, state_->field);
+    state_->show_state = ShowState::kCanBeShown;
   }
-
-  // Transition to the SHOWN state.
-
-  if (state_->segmentation_result) {
-    engagement_trackers_[state_->field.renderer_id] =
-        std::make_unique<EngagementTracker>(
-            state_->field.renderer_id, state_->segmentation_result->request_id,
-            this);
-  }
-  delegate_->ShowProactiveNudge(state_->form, state_->field);
-  state_->show_state = ShowState::kCanBeShown;
 }
 
 void ProactiveNudgeTracker::GotClassificationResult(
@@ -233,46 +148,14 @@
     ResetState();
     return;
   }
-  state->segmentation_result = std::move(result);
-
+  state->segmentation_result = result.ordered_labels[0] ==
+                               segmentation_platform::kComposePrmotionLabelShow;
   MaybeShowProactiveNudge();
 }
 
-void ProactiveNudgeTracker::CollectTrainingData(
-    const segmentation_platform::TrainingRequestId training_request_id,
-    ProactiveNudgeDerivedEngagement engagement) {
-  segmentation_platform::TrainingLabels training_labels;
-  // TODO(harringtond): Add UMA for Compose.ProactiveNudge.DerivedEngagement.
-  training_labels.output_metric =
-      std::make_pair("Compose.ProactiveNudge.DerivedEngagement",
-                     static_cast<base::HistogramBase::Sample>(engagement));
-  segmentation_service_->CollectTrainingData(
-      segmentation_platform::proto::SegmentId::
-          OPTIMIZATION_TARGET_SEGMENTATION_COMPOSE_PROMOTION,
-      training_request_id, training_labels, base::DoNothing());
-}
-
 bool ProactiveNudgeTracker::MatchesCurrentField(autofill::FormGlobalId form,
                                                 autofill::FieldGlobalId field) {
   return state_ && state_->form == form && state_->field == field;
 }
 
-void ProactiveNudgeTracker::ComposeSessionCompleted(
-    autofill::FieldRendererId field_renderer_id,
-    ComposeSessionCloseReason session_close_reason,
-    const compose::ComposeSessionEvents& events) {
-  auto iter = engagement_trackers_.find(field_renderer_id);
-  if (iter != engagement_trackers_.end()) {
-    iter->second->ComposeSessionCompleted(session_close_reason, events);
-    engagement_trackers_.erase(iter);
-  }
-}
-
-void ProactiveNudgeTracker::OnUserDisabledNudge(bool single_site_only) {
-  for (auto& iter : engagement_trackers_) {
-    iter.second->UserDisabledNudge(single_site_only);
-  }
-  engagement_trackers_.clear();
-}
-
 }  // namespace compose
diff --git a/chrome/browser/compose/proactive_nudge_tracker.h b/chrome/browser/compose/proactive_nudge_tracker.h
index 0d0f962..41e2b10 100644
--- a/chrome/browser/compose/proactive_nudge_tracker.h
+++ b/chrome/browser/compose/proactive_nudge_tracker.h
@@ -5,18 +5,14 @@
 #ifndef CHROME_BROWSER_COMPOSE_PROACTIVE_NUDGE_TRACKER_H_
 #define CHROME_BROWSER_COMPOSE_PROACTIVE_NUDGE_TRACKER_H_
 
-#include <map>
-#include <memory>
 #include <optional>
 #include <string>
 
-#include "base/functional/callback_forward.h"
 #include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/content/browser/scoped_autofill_managers_observation.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/common/unique_ids.h"
-#include "components/compose/core/browser/compose_metrics.h"
 #include "components/segmentation_platform/public/segmentation_platform_service.h"
 
 namespace compose {
@@ -61,8 +57,7 @@
     autofill::FormGlobalId form;
     autofill::FieldGlobalId field;
     std::u16string initial_text_value;
-    std::optional<segmentation_platform::ClassificationResult>
-        segmentation_result = std::nullopt;
+    std::optional<bool> segmentation_result = std::nullopt;
     base::OneShotTimer timer;
     bool timer_complete = false;
 
@@ -93,20 +88,12 @@
 
   void FocusChangedInPage();
 
-  void Clear();
-
-  void ComposeSessionCompleted(autofill::FieldRendererId field_renderer_id,
-                               ComposeSessionCloseReason session_close_reason,
-                               const compose::ComposeSessionEvents& events);
-  void OnUserDisabledNudge(bool single_site_only);
-
   // autofill::AutofillManager::Observer:
   void OnAfterFocusOnFormField(autofill::AutofillManager& manager,
                                autofill::FormGlobalId form,
                                autofill::FieldGlobalId field) override;
 
  private:
-  class EngagementTracker;
   bool SegmentationStateIsValid();
   void ResetState();
   void ShowTimerElapsed();
@@ -116,16 +103,9 @@
   void MaybeShowProactiveNudge();
   bool MatchesCurrentField(autofill::FormGlobalId form,
                            autofill::FieldGlobalId field);
-  void CollectTrainingData(
-      const segmentation_platform::TrainingRequestId training_request_id,
-      ProactiveNudgeDerivedEngagement engagement);
-  bool ShouldShow(const State& state);
 
   std::unique_ptr<State> state_;
 
-  std::map<autofill::FieldRendererId, std::unique_ptr<EngagementTracker>>
-      engagement_trackers_;
-
   raw_ptr<segmentation_platform::SegmentationPlatformService>
       segmentation_service_;
   raw_ptr<Delegate> delegate_;
diff --git a/chrome/browser/compose/proactive_nudge_tracker_unittest.cc b/chrome/browser/compose/proactive_nudge_tracker_unittest.cc
index 38ea9a3..4166b353 100644
--- a/chrome/browser/compose/proactive_nudge_tracker_unittest.cc
+++ b/chrome/browser/compose/proactive_nudge_tracker_unittest.cc
@@ -4,69 +4,48 @@
 
 #include "chrome/browser/compose/proactive_nudge_tracker.h"
 
-#include <memory>
-
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "components/autofill/core/common/autofill_test_utils.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill/core/common/unique_ids.h"
-#include "components/compose/core/browser/compose_metrics.h"
 #include "components/compose/core/browser/config.h"
 #include "components/segmentation_platform/public/constants.h"
 #include "components/segmentation_platform/public/testing/mock_segmentation_platform_service.h"
-#include "components/segmentation_platform/public/trigger.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::_;
 
 namespace compose {
-namespace {
-
-using base::test::TestFuture;
-using testing::_;
-const autofill::FieldRendererId kFieldRendererId(123);
-const autofill::FieldRendererId kFieldRendererId2(4);
-
-segmentation_platform::TrainingRequestId TrainingRequestId(
-    int request_number = 0) {
-  return segmentation_platform::TrainingRequestId(request_number + 456);
-}
-
-autofill::FormFieldData CreateTestFormFieldData(
-    autofill::FieldRendererId renderer_id = kFieldRendererId) {
-  autofill::FormFieldData f;
-  f.set_host_frame(autofill::test::MakeLocalFrameToken());
-  f.set_renderer_id(renderer_id);
-  f.set_value(u"FormFieldDataInitialValue");
-  return f;
-}
 
 class MockProactiveNudgeTrackerDelegate
     : public ProactiveNudgeTracker::Delegate {
  public:
   MOCK_METHOD(void,
               ShowProactiveNudge,
-              (autofill::FormGlobalId, autofill::FieldGlobalId));
+              (autofill::FormGlobalId, autofill::FieldGlobalId),
+              (override));
 };
 
-class ProactiveNudgeTrackerTestBase : public testing::Test {
+class ProactiveNudgeTrackerTest : public testing::TestWithParam<bool> {
  public:
-  ProactiveNudgeTrackerTestBase() = default;
+  ProactiveNudgeTrackerTest() = default;
 
-  ProactiveNudgeTrackerTestBase(const ProactiveNudgeTrackerTestBase&) = delete;
-  ProactiveNudgeTrackerTestBase& operator=(
-      const ProactiveNudgeTrackerTestBase&) = delete;
+  ProactiveNudgeTrackerTest(const ProactiveNudgeTrackerTest&) = delete;
+  ProactiveNudgeTrackerTest& operator=(const ProactiveNudgeTrackerTest&) =
+      delete;
 
-  ~ProactiveNudgeTrackerTestBase() override = default;
+  ~ProactiveNudgeTrackerTest() override = default;
 
-  void SetUpNudgeTrackerTest(bool use_segmentation) {
+  void SetUp() override {
+    testing::TestWithParam<bool>::SetUp();
     compose::GetMutableConfigForTesting().proactive_nudge_segmentation =
-        use_segmentation;
+        GetParam();
     nudge_tracker_ = std::make_unique<ProactiveNudgeTracker>(
         &segmentation_service_, &delegate_);
 
-    if (use_segmentation) {
+    if (uses_segmentation()) {
       SetSegmentationResult();
     } else {
       EXPECT_CALL(segmentation_service(), GetClassificationResult(_, _, _, _))
@@ -75,6 +54,7 @@
   }
 
   void TearDown() override {
+    testing::TestWithParam<bool>::TearDown();
     compose::ResetConfigForTesting();
   }
 
@@ -88,14 +68,21 @@
   }
   ProactiveNudgeTracker& nudge_tracker() { return *nudge_tracker_; }
 
+  autofill::FormFieldData CreateTestFormFieldData() {
+    autofill::FormFieldData f;
+    f.set_host_frame(autofill::test::MakeLocalFrameToken());
+    f.set_renderer_id(autofill::FieldRendererId(123));
+    f.set_value(u"FormFieldDataInitialValue");
+    return f;
+  }
+
   void SetSegmentationResult(std::string label = "Show") {
     ON_CALL(segmentation_service(), GetClassificationResult(_, _, _, _))
         .WillByDefault(testing::WithArg<3>(testing::Invoke(
-            [label, this](
+            [label](
                 segmentation_platform::ClassificationResultCallback callback) {
               auto result = segmentation_platform::ClassificationResult(
                   segmentation_platform::PredictionStatus::kSucceeded);
-              result.request_id = TrainingRequestId(training_request_number_++);
               result.ordered_labels = {label};
               std::move(callback).Run(result);
             })));
@@ -113,6 +100,8 @@
             })));
   }
 
+  bool uses_segmentation() { return GetParam(); }
+
  private:
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
@@ -123,21 +112,6 @@
   testing::NiceMock<segmentation_platform::MockSegmentationPlatformService>
       segmentation_service_;
   std::unique_ptr<ProactiveNudgeTracker> nudge_tracker_;
-  int training_request_number_ = 0;
-};
-
-class ProactiveNudgeTrackerTest : public ProactiveNudgeTrackerTestBase,
-                                  public testing::WithParamInterface<bool> {
- public:
-  ProactiveNudgeTrackerTest() = default;
-
-  ~ProactiveNudgeTrackerTest() override = default;
-
-  void SetUp() override {
-    ProactiveNudgeTrackerTestBase::SetUpNudgeTrackerTest(uses_segmentation());
-  }
-
-  bool uses_segmentation() { return GetParam(); }
 };
 
 TEST_P(ProactiveNudgeTrackerTest, TestWait) {
@@ -154,7 +128,7 @@
   // Should not nudge if nudge is requested too soon.
   EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
 
-  task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
+  task_environment().FastForwardBy(base::Seconds(4));
   if (uses_segmentation()) {
     EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
     auto result = segmentation_platform::ClassificationResult(
@@ -185,7 +159,7 @@
   // Should not nudge if nudge is requested too soon.
   EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
 
-  task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
+  task_environment().FastForwardBy(base::Seconds(4));
   EXPECT_TRUE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
 }
 
@@ -198,7 +172,7 @@
   EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
   nudge_tracker().FocusChangedInPage();
 
-  task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
+  task_environment().FastForwardBy(base::Seconds(4));
   EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
 }
 
@@ -215,7 +189,7 @@
 
   EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
   EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field2));
-  task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
+  task_environment().FastForwardBy(base::Seconds(4));
   EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
 }
 
@@ -226,7 +200,7 @@
       .Times(0);
 
   nudge_tracker().FocusChangedInPage();
-  task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
+  task_environment().FastForwardBy(base::Seconds(4));
 }
 
 TEST_P(ProactiveNudgeTrackerTest, TestNoNudgeDelay) {
@@ -252,7 +226,7 @@
         .Times(0);
     EXPECT_TRUE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
     // Wait just in case the timer could be pending.
-    task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
+    task_environment().FastForwardBy(base::Seconds(4));
   }
 }
 
@@ -269,7 +243,7 @@
       .Times(uses_segmentation() ? 0 : 1);
 
   EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
-  task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
+  task_environment().FastForwardBy(base::Seconds(4));
 
   if (uses_segmentation()) {
     EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
@@ -284,194 +258,7 @@
             nudge_tracker().ProactiveNudgeRequestedForFormField(field));
 }
 
-INSTANTIATE_TEST_SUITE_P(,
+INSTANTIATE_TEST_SUITE_P(ProactiveNudgeTrackerTest,
                          ProactiveNudgeTrackerTest,
-                         ::testing::Bool(),
-                         [](const auto& info) {
-                           return info.param ? "SegmentationON"
-                                             : "SegmentationOFF";
-                         });
-
-class ProactiveNudgeTrackerDerivedEngagementTest
-    : public ProactiveNudgeTrackerTestBase {
- public:
-  void SetUp() override {
-    ProactiveNudgeTrackerTestBase::SetUpNudgeTrackerTest(true);
-  }
-
-  // Set up a scenario where the nudge is shown for a field.
-  TestFuture<segmentation_platform::TrainingLabels>& TriggerNudgeForField(
-      int request_number,
-      const autofill::FormFieldData& field) {
-    EXPECT_CALL(delegate(),
-                ShowProactiveNudge(field.renderer_form_id(), field.global_id()))
-        .Times(1);
-
-    EXPECT_FALSE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
-    task_environment().FastForwardBy(GetComposeConfig().proactive_nudge_delay);
-    EXPECT_TRUE(nudge_tracker().ProactiveNudgeRequestedForFormField(field));
-
-    TestFuture<segmentation_platform::TrainingLabels>& training_labels =
-        training_labels_futures_.emplace_back();
-    EXPECT_CALL(segmentation_service(),
-                CollectTrainingData(
-                    segmentation_platform::proto::SegmentId::
-                        OPTIMIZATION_TARGET_SEGMENTATION_COMPOSE_PROMOTION,
-                    TrainingRequestId(request_number), _, _))
-        .Times(1)
-        .WillOnce(testing::Invoke([&](auto, auto, auto labels, auto) {
-          training_labels.SetValue(labels);
-        }));
-    return training_labels;
-  }
-
-  // Destroy the nudge tracker. This triggers CollectTrainingData() if
-  // necessary.
-  void Reset() { SetUpNudgeTrackerTest(true); }
-
- private:
-  // Just holds memory for futures created in TriggerNudgeForField(), not for
-  // direct use.
-  std::deque<TestFuture<segmentation_platform::TrainingLabels>>
-      training_labels_futures_;
-};
-
-TEST_F(ProactiveNudgeTrackerDerivedEngagementTest, NoEngagement) {
-  TestFuture<segmentation_platform::TrainingLabels>& training_labels =
-      TriggerNudgeForField(0, CreateTestFormFieldData());
-  Reset();
-
-  EXPECT_EQ(training_labels.Get().output_metric,
-            std::make_pair("Compose.ProactiveNudge.DerivedEngagement",
-                           static_cast<base::HistogramBase::Sample>(
-                               ProactiveNudgeDerivedEngagement::kIgnored)));
-}
-
-TEST_F(ProactiveNudgeTrackerDerivedEngagementTest, MinimalUse) {
-  TestFuture<segmentation_platform::TrainingLabels>& training_labels =
-      TriggerNudgeForField(0, CreateTestFormFieldData());
-  compose::ComposeSessionEvents events;
-  nudge_tracker().ComposeSessionCompleted(
-      kFieldRendererId, ComposeSessionCloseReason::kCloseButtonPressed, events);
-  Reset();
-
-  EXPECT_EQ(
-      training_labels.Get().output_metric,
-      std::make_pair(
-          "Compose.ProactiveNudge.DerivedEngagement",
-          static_cast<base::HistogramBase::Sample>(
-              ProactiveNudgeDerivedEngagement::kOpenedComposeMinimalUse)));
-}
-
-TEST_F(ProactiveNudgeTrackerDerivedEngagementTest, SuggestionGenerated) {
-  TestFuture<segmentation_platform::TrainingLabels>& training_labels =
-      TriggerNudgeForField(0, CreateTestFormFieldData());
-  compose::ComposeSessionEvents events;
-  events.compose_count = 1;
-  nudge_tracker().ComposeSessionCompleted(
-      kFieldRendererId, ComposeSessionCloseReason::kCloseButtonPressed, events);
-  // This test should work with or without deleting the tracker.
-  Reset();
-
-  EXPECT_EQ(
-      training_labels.Get().output_metric,
-      std::make_pair(
-          "Compose.ProactiveNudge.DerivedEngagement",
-          static_cast<base::HistogramBase::Sample>(
-              ProactiveNudgeDerivedEngagement::kGeneratedComposeSuggestion)));
-}
-
-TEST_F(ProactiveNudgeTrackerDerivedEngagementTest, AcceptedSuggestion) {
-  TestFuture<segmentation_platform::TrainingLabels>& training_labels =
-      TriggerNudgeForField(0, CreateTestFormFieldData());
-  compose::ComposeSessionEvents events;
-  events.compose_count = 1;
-  events.inserted_results = true;
-  nudge_tracker().ComposeSessionCompleted(
-      kFieldRendererId, ComposeSessionCloseReason::kAcceptedSuggestion, events);
-
-  EXPECT_EQ(
-      training_labels.Get().output_metric,
-      std::make_pair(
-          "Compose.ProactiveNudge.DerivedEngagement",
-          static_cast<base::HistogramBase::Sample>(
-              ProactiveNudgeDerivedEngagement::kAcceptedComposeSuggestion)));
-}
-
-TEST_F(ProactiveNudgeTrackerDerivedEngagementTest,
-       IgnoresSessionForDifferentField) {
-  TestFuture<segmentation_platform::TrainingLabels>& training_labels =
-      TriggerNudgeForField(0, CreateTestFormFieldData());
-  compose::ComposeSessionEvents events;
-  // This one is ignored because it has the wrong field id.
-  nudge_tracker().ComposeSessionCompleted(
-      autofill::FieldRendererId(999),
-      ComposeSessionCloseReason::kEndedImplicitly, events);
-
-  events.compose_count = 1;
-  events.inserted_results = true;
-  nudge_tracker().ComposeSessionCompleted(
-      kFieldRendererId, ComposeSessionCloseReason::kAcceptedSuggestion, events);
-
-  EXPECT_EQ(
-      training_labels.Get().output_metric,
-      std::make_pair(
-          "Compose.ProactiveNudge.DerivedEngagement",
-          static_cast<base::HistogramBase::Sample>(
-              ProactiveNudgeDerivedEngagement::kAcceptedComposeSuggestion)));
-}
-
-TEST_F(ProactiveNudgeTrackerDerivedEngagementTest, TwoSessions) {
-  TestFuture<segmentation_platform::TrainingLabels>& training_labels1 =
-      TriggerNudgeForField(0, CreateTestFormFieldData());
-  TestFuture<segmentation_platform::TrainingLabels>& training_labels2 =
-      TriggerNudgeForField(1, CreateTestFormFieldData(kFieldRendererId2));
-  compose::ComposeSessionEvents events;
-  events.compose_count = 1;
-  events.inserted_results = true;
-  nudge_tracker().ComposeSessionCompleted(
-      kFieldRendererId, ComposeSessionCloseReason::kAcceptedSuggestion, events);
-  Reset();
-
-  EXPECT_EQ(
-      training_labels1.Get().output_metric,
-      std::make_pair(
-          "Compose.ProactiveNudge.DerivedEngagement",
-          static_cast<base::HistogramBase::Sample>(
-              ProactiveNudgeDerivedEngagement::kAcceptedComposeSuggestion)));
-  EXPECT_EQ(training_labels2.Get().output_metric,
-            std::make_pair("Compose.ProactiveNudge.DerivedEngagement",
-                           static_cast<base::HistogramBase::Sample>(
-                               ProactiveNudgeDerivedEngagement::kIgnored)));
-}
-
-TEST_F(ProactiveNudgeTrackerDerivedEngagementTest, NudgeDisabledSingleSite) {
-  TestFuture<segmentation_platform::TrainingLabels>& training_labels =
-      TriggerNudgeForField(0, CreateTestFormFieldData());
-  nudge_tracker().OnUserDisabledNudge(/*single_site_only=*/true);
-  Reset();
-
-  EXPECT_EQ(
-      training_labels.Get().output_metric,
-      std::make_pair(
-          "Compose.ProactiveNudge.DerivedEngagement",
-          static_cast<base::HistogramBase::Sample>(
-              ProactiveNudgeDerivedEngagement::kNudgeDisabledOnSingleSite)));
-}
-
-TEST_F(ProactiveNudgeTrackerDerivedEngagementTest, NudgeDisabledAllSites) {
-  TestFuture<segmentation_platform::TrainingLabels>& training_labels =
-      TriggerNudgeForField(0, CreateTestFormFieldData());
-  nudge_tracker().OnUserDisabledNudge(/*single_site_only=*/false);
-  Reset();
-
-  EXPECT_EQ(
-      training_labels.Get().output_metric,
-      std::make_pair(
-          "Compose.ProactiveNudge.DerivedEngagement",
-          static_cast<base::HistogramBase::Sample>(
-              ProactiveNudgeDerivedEngagement::kNudgeDisabledOnAllSites)));
-}
-
-}  // namespace
+                         ::testing::Bool());
 }  // namespace compose
diff --git a/chrome/browser/media_galleries/chromeos/mtp_device_delegate_impl_chromeos.cc b/chrome/browser/media_galleries/chromeos/mtp_device_delegate_impl_chromeos.cc
index 755c9b0..3c717e29 100644
--- a/chrome/browser/media_galleries/chromeos/mtp_device_delegate_impl_chromeos.cc
+++ b/chrome/browser/media_galleries/chromeos/mtp_device_delegate_impl_chromeos.cc
@@ -1082,13 +1082,8 @@
     const base::File::Info& source_file_info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
-  if (source_file_info.is_directory) {
-    std::move(error_callback).Run(base::File::FILE_ERROR_NOT_A_FILE);
-    return;
-  }
-
   if (source_file_path.DirName() == device_file_path.DirName()) {
-    // If a file is moved in a same directory, rename the file.
+    // If a file or directory is moved in a same directory, rename it.
     std::optional<uint32_t> file_id = CachedPathToId(source_file_path);
     if (file_id) {
       MTPDeviceTaskHelper::RenameObjectSuccessCallback
@@ -1109,11 +1104,19 @@
                                            content::BrowserThread::UI,
                                            FROM_HERE, std::move(closure)));
     } else {
-      std::move(error_callback).Run(base::File::FILE_ERROR_NOT_FOUND);
+      std::move(error_callback)
+          .Run(source_file_info.is_directory
+                   ? base::File::FILE_ERROR_NOT_A_FILE
+                   : base::File::FILE_ERROR_NOT_FOUND);
     }
     return;
   }
 
+  if (source_file_info.is_directory) {
+    std::move(error_callback).Run(base::File::FILE_ERROR_NOT_A_FILE);
+    return;
+  }
+
   // In case of error, only one callback will be called.
   auto split_error_callback =
       base::SplitOnceCallback(std::move(error_callback));
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn
index 03da120..dce18db 100644
--- a/chrome/browser/password_manager/android/BUILD.gn
+++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -262,6 +262,7 @@
     ":unified_password_manager_proto_java",
     "$google_play_services_package:google_play_services_base_java",
     "$google_play_services_package:google_play_services_basement_java",
+    "account_storage_notice:junit",
     "add_username_dialog:junit",
     "pwd_migration:junit",
     "//base:base_java",
@@ -547,7 +548,6 @@
     ":backend_public",
     ":unified_password_manager_proto",
     ":utils",
-    "account_storage_notice:unit_tests",
     "//base/test:test_support",
     "//chrome/app:generated_resources",
     "//chrome/browser",
diff --git a/chrome/browser/password_manager/android/account_storage_notice/BUILD.gn b/chrome/browser/password_manager/android/account_storage_notice/BUILD.gn
index f965292..d246e5a 100644
--- a/chrome/browser/password_manager/android/account_storage_notice/BUILD.gn
+++ b/chrome/browser/password_manager/android/account_storage_notice/BUILD.gn
@@ -18,11 +18,8 @@
     "//base",
     "//chrome/android:chrome_jni_headers",
     "//components/prefs",
-    "//components/sync/base",
     "//components/sync/service",
-    "//content/public/browser",
     "//ui/android",
-    "//ui/gfx:native_widget_types",
   ]
 }
 
@@ -39,9 +36,13 @@
   deps = [
     ":resources",
     "//base:base_java",
+    "//chrome/browser/flags:java",
+    "//chrome/browser/preferences:java",
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//components/browser_ui/bottomsheet/android:java",
     "//components/browser_ui/settings/android:java",
+    "//components/prefs/android:java",
+    "//components/sync/android:sync_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/jni_zero:jni_zero_java",
     "//ui/android:ui_java",
@@ -72,33 +73,46 @@
   visibility = [ ":*" ]
 }
 
-source_set("unit_tests") {
+robolectric_library("junit") {
   testonly = true
-  sources = [ "account_storage_notice_unittest.cc" ]
+  sources = [ "junit/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorUnitTest.java" ]
   deps = [
-    ":account_storage_notice",
-    "//base/test:test_support",
-    "//components/password_manager/core/common",
-    "//components/prefs",
-    "//components/prefs:test_support",
-    "//components/sync:test_support",
-    "//components/sync/base",
-    "//testing/gtest",
+    ":java",
+    "//base:base_java_test_support",
+    "//base:base_junit_test_support",
+    "//chrome/browser/flags:java",
+    "//chrome/browser/preferences:java",
+    "//components/browser_ui/bottomsheet/android:java",
+    "//components/browser_ui/settings/android:java",
+    "//components/prefs/android:java",
+    "//components/sync/android:sync_java",
+    "//third_party/androidx:androidx_test_runner_java",
+    "//third_party/junit:junit",
+    "//third_party/mockito:mockito_java",
+    "//ui/android:ui_java",
   ]
 }
 
 android_library("javatests") {
   testonly = true
-  sources = [ "javatests/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorTest.java" ]
+  sources = [ "javatests/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorIntegrationTest.java" ]
   deps = [
     ":java",
     "//base:base_java_test_support",
     "//chrome/android:chrome_java",
     "//chrome/browser/flags:java",
+    "//chrome/browser/preferences:java",
+    "//chrome/browser/profiles/android:java",
+    "//chrome/browser/sync/android:java",
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//chrome/test/android:chrome_java_integration_test_support",
     "//components/browser_ui/bottomsheet/android:java",
     "//components/browser_ui/settings/android:java",
+    "//components/prefs/android:java",
+    "//components/signin/public/android:java",
+    "//components/sync/android:sync_java",
+    "//components/user_prefs/android:java",
+    "//content/public/android:content_full_java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:espresso_java",
     "//third_party/androidx:androidx_test_runner_java",
diff --git a/chrome/browser/password_manager/android/account_storage_notice/account_storage_notice.cc b/chrome/browser/password_manager/android/account_storage_notice/account_storage_notice.cc
index 9070dc78..898f0ad 100644
--- a/chrome/browser/password_manager/android/account_storage_notice/account_storage_notice.cc
+++ b/chrome/browser/password_manager/android/account_storage_notice/account_storage_notice.cc
@@ -8,63 +8,58 @@
 
 #include "base/android/jni_android.h"
 #include "base/check.h"
-#include "base/feature_list.h"
+#include "base/memory/ptr_util.h"
 #include "chrome/android/chrome_jni_headers/SettingsLauncherImpl_jni.h"
 #include "chrome/browser/password_manager/android/account_storage_notice/jni/AccountStorageNoticeCoordinator_jni.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/sync/base/features.h"
-#include "components/sync/base/user_selectable_type.h"
 #include "components/sync/service/sync_service.h"
-#include "components/sync/service/sync_user_settings.h"
-#include "content/public/browser/web_contents.h"
 #include "ui/android/window_android.h"
-#include "ui/gfx/native_widget_types.h"
 
 using base::android::AttachCurrentThread;
 
 // static
-bool AccountStorageNotice::ShouldShow(PrefService* pref_service,
-                                      syncer::SyncService* sync_service) {
-  // TODO(crbug.com/338576301): Consider checking UPM predicates here too.
-  return sync_service && !sync_service->HasSyncConsent() &&
-         sync_service->GetUserSettings()->GetSelectedTypes().Has(
-             syncer::UserSelectableType::kPasswords) &&
-         !pref_service->GetBoolean(
-             password_manager::prefs::kAccountStorageNoticeShown) &&
-         base::FeatureList::IsEnabled(
-             syncer::kEnablePasswordsAccountStorageForNonSyncingUsers);
+std::unique_ptr<AccountStorageNotice> AccountStorageNotice::MaybeShow(
+    syncer::SyncService* sync_service,
+    PrefService* pref_service,
+    ui::WindowAndroid* window_android,
+    base::OnceClosure done_cb) {
+  base::android::ScopedJavaLocalRef<jobject> java_coordinator =
+      Java_AccountStorageNoticeCoordinator_create(
+          AttachCurrentThread(),
+          sync_service ? sync_service->GetJavaObject() : nullptr,
+          pref_service->GetJavaObject(), window_android->GetJavaObject(),
+          Java_SettingsLauncherImpl_create(AttachCurrentThread()));
+  if (java_coordinator) {
+    return base::WrapUnique(
+        new AccountStorageNotice(java_coordinator, std::move(done_cb)));
+  }
+  // Creation failed, reply immediately.
+  std::move(done_cb).Run();
+  return nullptr;
 }
 
-AccountStorageNotice::AccountStorageNotice(content::WebContents* web_contents,
-                                           PrefService* pref_service,
-                                           syncer::SyncService* sync_service,
-                                           base::OnceClosure closed_cb)
-    : java_coordinator_(Java_AccountStorageNoticeCoordinator_Constructor(
-          AttachCurrentThread(),
-          web_contents->GetNativeView()->GetWindowAndroid()->GetJavaObject(),
-          Java_SettingsLauncherImpl_create(AttachCurrentThread()),
-          reinterpret_cast<intptr_t>(this))),
-      closed_cb_(std::move(closed_cb)) {
-  CHECK(closed_cb_);
-  CHECK(ShouldShow(pref_service, sync_service));
-  pref_service->SetBoolean(password_manager::prefs::kAccountStorageNoticeShown,
-                           true);
+AccountStorageNotice::AccountStorageNotice(
+    base::android::ScopedJavaLocalRef<jobject> java_coordinator,
+    base::OnceClosure done_cb)
+    : java_coordinator_(java_coordinator), done_cb_(std::move(done_cb)) {
+  CHECK(java_coordinator_);
+  CHECK(done_cb_);
+  Java_AccountStorageNoticeCoordinator_setObserver(
+      AttachCurrentThread(), java_coordinator_,
+      reinterpret_cast<intptr_t>(this));
 }
 
 AccountStorageNotice::~AccountStorageNotice() {
-  if (java_coordinator_) {
-    // See destructor docs as to when this can happen.
-    Java_AccountStorageNoticeCoordinator_destroy(AttachCurrentThread(),
-                                                 java_coordinator_);
-  }
+  // Remove the observer *before* calling hideImmediatelyIfShowing(), because we
+  // don't want to trigger OnClosed() and `done_cb` if the sheet was still
+  // showing.
+  Java_AccountStorageNoticeCoordinator_setObserver(AttachCurrentThread(),
+                                                   java_coordinator_, 0);
+  Java_AccountStorageNoticeCoordinator_hideImmediatelyIfShowing(
+      AttachCurrentThread(), java_coordinator_);
 }
 
 void AccountStorageNotice::OnClosed(JNIEnv* env) {
-  CHECK(java_coordinator_);
-  Java_AccountStorageNoticeCoordinator_destroy(AttachCurrentThread(),
-                                               java_coordinator_);
-  java_coordinator_.Reset();
-  std::move(closed_cb_).Run();
-  // `closed_cb_` might have deleted the object above, do nothing else.
+  std::move(done_cb_).Run();
+  // `done_cb_` might have deleted the object above, do nothing else.
 }
diff --git a/chrome/browser/password_manager/android/account_storage_notice/account_storage_notice.h b/chrome/browser/password_manager/android/account_storage_notice/account_storage_notice.h
index 3191f25..f455e31 100644
--- a/chrome/browser/password_manager/android/account_storage_notice/account_storage_notice.h
+++ b/chrome/browser/password_manager/android/account_storage_notice/account_storage_notice.h
@@ -11,14 +11,14 @@
 #include "base/functional/callback.h"
 #include "chrome/browser/password_manager/android/account_storage_notice/coordinator_observer.h"
 
-namespace content {
-class WebContents;
-}
-
 namespace syncer {
 class SyncService;
 }
 
+namespace ui {
+class WindowAndroid;
+}
+
 class PrefService;
 
 // A bottom sheet that informs the user they are now saving & filling passwords
@@ -30,36 +30,45 @@
 // The sheet can also be closed by swiping, pressing back or navigating away.
 class AccountStorageNotice : public CoordinatorObserver {
  public:
-  // Whether the one-off notice should be shown.
-  static bool ShouldShow(PrefService* pref_service,
-                         syncer::SyncService* sync_service);
-
-  // Shows the notice (embedders are responsible for checking ShouldShow()
-  // first). `closed_cb` must be non-null and will be invoked once the
-  // sheet is closed by user interaction. It will not be invoked if the object
-  // is prematurely destroyed, see note in the destructor.
-  // The object does nothing else after invoking the callback, so it's safe for
-  // the callback to destroy the object.
-  // After calling this, ShouldShow() returns false forever.
-  AccountStorageNotice(content::WebContents* web_contents,
-                       PrefService* pref_service,
-                       syncer::SyncService* sync_service,
-                       base::OnceClosure closed_cb);
+  // This notice should only be shown to users who are signed-in, not syncing,
+  // have the passwords data type enabled and never saw it before.
+  // If such conditions are not satisfied, this method:
+  //   - Shows nothing.
+  //   - Invokes `done_cb` immediately.
+  //   - Returns null.
+  // Otherwise, it:
+  //   - Shows the notice.
+  //   - Will invoke `done_cb` once the notice is closed by user interaction.
+  //   - Returns an object that must be stored by the embedder. `done_cb` can
+  //     safely delete the object because:
+  //       a) The implementation does nothing else after the invocation.
+  //       b) If the object is prematurely destroyed, the notice will close
+  //          promptly without invoking the callback. So there will be no double
+  //          destruction.
+  static std::unique_ptr<AccountStorageNotice> MaybeShow(
+      syncer::SyncService* sync_service,
+      PrefService* pref_service,
+      ui::WindowAndroid* window_android,
+      base::OnceClosure done_cb);
 
   AccountStorageNotice(const AccountStorageNotice&) = delete;
   AccountStorageNotice& operator=(const AccountStorageNotice&) = delete;
 
-  // By the time this object is destroyed, normally the sheet should have been
-  // closed and `closed_cb` invoked. If that didn't happen, the destructor will
-  // hide the sheet promptly without invoking `closed_cb`.
+  // By the time this object is destroyed, normally the notice should have been
+  // closed and `done_cb` invoked. If that didn't happen, the destructor will
+  // hide the notice promptly without invoking `done_cb`.
   ~AccountStorageNotice() override;
 
  private:
+  AccountStorageNotice(
+      base::android::ScopedJavaLocalRef<jobject> java_coordinator,
+      base::OnceClosure done_cb);
+
   void OnClosed(JNIEnv* env) override;
 
-  base::android::ScopedJavaGlobalRef<jobject> java_coordinator_;
+  const base::android::ScopedJavaGlobalRef<jobject> java_coordinator_;
 
-  base::OnceClosure closed_cb_;
+  base::OnceClosure done_cb_;
 };
 
 #endif  // CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_ACCOUNT_STORAGE_NOTICE_ACCOUNT_STORAGE_NOTICE_H_
diff --git a/chrome/browser/password_manager/android/account_storage_notice/account_storage_notice_unittest.cc b/chrome/browser/password_manager/android/account_storage_notice/account_storage_notice_unittest.cc
deleted file mode 100644
index 287f6826..0000000
--- a/chrome/browser/password_manager/android/account_storage_notice/account_storage_notice_unittest.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// 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.
-
-#include "chrome/browser/password_manager/android/account_storage_notice/account_storage_notice.h"
-
-#include "base/test/scoped_feature_list.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/sync/base/features.h"
-#include "components/sync/base/user_selectable_type.h"
-#include "components/sync/test/test_sync_service.h"
-#include "components/sync/test/test_sync_user_settings.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-class AccountStorageNoticeTest : public ::testing::Test {
- public:
-  AccountStorageNoticeTest() {
-    pref_service_.registry()->RegisterBooleanPref(
-        password_manager::prefs::kAccountStorageNoticeShown, false);
-  }
-
-  TestingPrefServiceSimple* pref_service() { return &pref_service_; }
-  syncer::TestSyncService* sync_service() { return &sync_service_; }
-
- private:
-  TestingPrefServiceSimple pref_service_;
-  syncer::TestSyncService sync_service_;
-};
-
-TEST_F(AccountStorageNoticeTest, ShouldNotShowIfSyncing) {
-  sync_service()->SetSignedInWithSyncFeatureOn();
-
-  EXPECT_FALSE(
-      AccountStorageNotice::ShouldShow(pref_service(), sync_service()));
-}
-
-TEST_F(AccountStorageNoticeTest, ShouldNotShowIfSignedOut) {
-  sync_service()->SetSignedOut();
-  // TODO(crbug.com/340502030): SetSignedOut() should empty the selected types.
-  sync_service()->GetUserSettings()->SetSelectedTypes(false, {});
-
-  EXPECT_FALSE(
-      AccountStorageNotice::ShouldShow(pref_service(), sync_service()));
-}
-
-TEST_F(AccountStorageNoticeTest, ShouldNotShowIfPasswordsDataTypeDisabled) {
-  sync_service()->SetSignedInWithoutSyncFeature();
-  sync_service()->GetUserSettings()->SetSelectedType(
-      syncer::UserSelectableType::kPasswords, false);
-
-  EXPECT_FALSE(
-      AccountStorageNotice::ShouldShow(pref_service(), sync_service()));
-}
-
-TEST_F(AccountStorageNoticeTest, ShouldNotShowIfAlreadyShown) {
-  sync_service()->SetSignedInWithoutSyncFeature();
-  pref_service()->SetBoolean(
-      password_manager::prefs::kAccountStorageNoticeShown, true);
-
-  EXPECT_FALSE(
-      AccountStorageNotice::ShouldShow(pref_service(), sync_service()));
-}
-
-TEST_F(AccountStorageNoticeTest, ShouldNotShowIfFlagDisabled) {
-  base::test::ScopedFeatureList disable_feature;
-  disable_feature.InitAndDisableFeature(
-      syncer::kEnablePasswordsAccountStorageForNonSyncingUsers);
-  sync_service()->SetSignedInWithoutSyncFeature();
-
-  EXPECT_FALSE(
-      AccountStorageNotice::ShouldShow(pref_service(), sync_service()));
-}
-
-TEST_F(AccountStorageNoticeTest, ShouldShow) {
-  base::test::ScopedFeatureList enable_feature(
-      syncer::kEnablePasswordsAccountStorageForNonSyncingUsers);
-  sync_service()->SetSignedInWithoutSyncFeature();
-
-  EXPECT_TRUE(AccountStorageNotice::ShouldShow(pref_service(), sync_service()));
-}
-
-}  // namespace
diff --git a/chrome/browser/password_manager/android/account_storage_notice/java/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinator.java b/chrome/browser/password_manager/android/account_storage_notice/java/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinator.java
index ab459af..74924f3 100644
--- a/chrome/browser/password_manager/android/account_storage_notice/java/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinator.java
+++ b/chrome/browser/password_manager/android/account_storage_notice/java/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinator.java
@@ -6,76 +6,109 @@
 
 import android.content.Context;
 
+import androidx.annotation.Nullable;
+
 import org.jni_zero.CalledByNative;
 import org.jni_zero.NativeMethods;
 
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
 import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
 import org.chromium.components.browser_ui.settings.SettingsLauncher;
 import org.chromium.components.browser_ui.settings.SettingsLauncher.SettingsFragment;
+import org.chromium.components.prefs.PrefService;
+import org.chromium.components.sync.SyncService;
+import org.chromium.components.sync.UserSelectableType;
 import org.chromium.ui.base.WindowAndroid;
 
 /** Coordinator for the UI described in account_storage_notice.h, meant to be used from native. */
-class AccountStorageNoticeCoordinator {
+class AccountStorageNoticeCoordinator extends EmptyBottomSheetObserver {
     private final SettingsLauncher mSettingsLauncher;
     private final Context mContext;
     private final BottomSheetController mBottomSheetController;
     private final AccountStorageNoticeView mView;
-    private final BottomSheetObserver mBottomSheetObserver =
-            new EmptyBottomSheetObserver() {
-                @Override
-                public void onSheetStateChanged(
-                        @SheetState int newState, @StateChangeReason int reason) {
-                    if (newState == SheetState.HIDDEN) {
-                        assert mNativeCoordinatorObserver != 0;
-                        AccountStorageNoticeCoordinatorJni.get()
-                                .onClosed(mNativeCoordinatorObserver);
-                    }
-                }
-            };
 
     private long mNativeCoordinatorObserver;
 
     @CalledByNative
-    public AccountStorageNoticeCoordinator(
+    public static @Nullable AccountStorageNoticeCoordinator create(
+            @Nullable SyncService syncService,
+            PrefService prefService,
             WindowAndroid windowAndroid,
-            SettingsLauncher settingsLauncher,
-            long nativeCoordinatorObserver) {
-        assert nativeCoordinatorObserver != 0;
+            SettingsLauncher settingsLauncher) {
+        boolean shouldShow =
+                syncService != null
+                        && !syncService.hasSyncConsent()
+                        && syncService.getSelectedTypes().contains(UserSelectableType.PASSWORDS)
+                        && !prefService.getBoolean(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN)
+                        && ChromeFeatureList.isEnabled(
+                                ChromeFeatureList
+                                        .ENABLE_PASSWORDS_ACCOUNT_STORAGE_FOR_NON_SYNCING_USERS);
+        if (!shouldShow) {
+            return null;
+        }
+
+        BottomSheetController controller = BottomSheetControllerProvider.from(windowAndroid);
+        AccountStorageNoticeView view =
+                new AccountStorageNoticeView(windowAndroid.getContext().get());
+        boolean success = controller.requestShowContent(view, /* animate= */ true);
+        if (!success) {
+            return null;
+        }
+
+        prefService.setBoolean(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN, true);
+        AccountStorageNoticeCoordinator coordinator =
+                new AccountStorageNoticeCoordinator(
+                        windowAndroid.getContext().get(), controller, view, settingsLauncher);
+        return coordinator;
+    }
+
+    private AccountStorageNoticeCoordinator(
+            Context context,
+            BottomSheetController bottomSheetController,
+            AccountStorageNoticeView view,
+            SettingsLauncher settingsLauncher) {
         mSettingsLauncher = settingsLauncher;
-        mContext = windowAndroid.getContext().get();
-        mBottomSheetController = BottomSheetControllerProvider.from(windowAndroid);
-        mNativeCoordinatorObserver = nativeCoordinatorObserver;
-        // No need for a ViewBinder to pass the callbacks, the logic is too simple.
-        mView =
-                new AccountStorageNoticeView(
-                        windowAndroid.getContext().get(),
-                        this::onButtonClicked,
-                        this::onSettingsLinkClicked);
-        mBottomSheetController.requestShowContent(mView, /* animate= */ true);
-        mBottomSheetController.addObserver(mBottomSheetObserver);
+        mContext = context;
+        mBottomSheetController = bottomSheetController;
+        mView = view;
+        mView.setButtonCallback(this::onButtonClicked);
+        mView.setSettingsLinkCallback(this::onSettingsLinkClicked);
+        mBottomSheetController.addObserver(this);
     }
 
     @CalledByNative
-    public void destroy() {
-        // Avoid dangling pointer.
-        mNativeCoordinatorObserver = 0;
-        mBottomSheetController.removeObserver(mBottomSheetObserver);
+    public void setObserver(long nativeCoordinatorObserver) {
+        mNativeCoordinatorObserver = nativeCoordinatorObserver;
+    }
+
+    /** If the notice is still showing, hides it promptly without animation. Otherwise, no-op. */
+    @CalledByNative
+    public void hideImmediatelyIfShowing() {
         if (mBottomSheetController.getCurrentSheetContent() == mView) {
-            // This means the owning C++ object was destroyed while the sheet was still showing. It
-            // should almost never happen. Note that it's important to remove the observer before
-            // calling hideContent(), so mBottomSheetObserver isn't triggered.
             mBottomSheetController.hideContent(mView, /* animate= */ false);
         }
     }
 
+    // EmptyBottomSheetObserver overrides.
+    @Override
+    public void onSheetStateChanged(@SheetState int newState, @StateChangeReason int reason) {
+        // This waits for the sheet to close, then stops the observation and notifies native.
+        if (newState == SheetState.HIDDEN) {
+            mBottomSheetController.removeObserver(this);
+            if (mNativeCoordinatorObserver != 0) {
+                AccountStorageNoticeCoordinatorJni.get().onClosed(mNativeCoordinatorObserver);
+            }
+        }
+    }
+
     private void onButtonClicked() {
         mBottomSheetController.hideContent(mView, /* animate= */ true);
-        // mBottomSheetObserver will take care of notifying native.
+        // onSheetStateChanged() will take care of notifying native.
     }
 
     private void onSettingsLinkClicked() {
diff --git a/chrome/browser/password_manager/android/account_storage_notice/java/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeView.java b/chrome/browser/password_manager/android/account_storage_notice/java/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeView.java
index 80e9e22..38887a6 100644
--- a/chrome/browser/password_manager/android/account_storage_notice/java/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeView.java
+++ b/chrome/browser/password_manager/android/account_storage_notice/java/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeView.java
@@ -19,15 +19,31 @@
 import org.chromium.ui.widget.ButtonCompat;
 
 class AccountStorageNoticeView implements BottomSheetContent {
+    private static boolean sSkipLayoutForTesting;
+
     private final View mContentView;
 
-    public AccountStorageNoticeView(
-            Context context, Runnable buttonCallback, Runnable settingsLinkCallback) {
+    // Initialized lazily by the corresponding setters.
+    private Runnable mButtonCallback;
+    private Runnable mSettingsLinkCallback;
+
+    // TODO(crbug.com/341176706): Shadow the AccountStorageNoticeView constructor in the unit test
+    // instead. There seems to be a problem with shadow and release builds.
+    public static void setSkipLayoutForTesting(boolean skip) {
+        sSkipLayoutForTesting = skip;
+    }
+
+    public AccountStorageNoticeView(Context context) {
+        if (sSkipLayoutForTesting) {
+            mContentView = null;
+            return;
+        }
+
         mContentView =
                 LayoutInflater.from(context)
                         .inflate(R.layout.account_storage_notice_layout, /* root= */ null);
         ((ButtonCompat) mContentView.findViewById(R.id.account_storage_notice_button))
-                .setOnClickListener(unused -> buttonCallback.run());
+                .setOnClickListener(unused -> mButtonCallback.run());
         TextView linkView = mContentView.findViewById(R.id.account_storage_settings_link);
         SpannableString linkText =
                 SpanApplier.applySpans(
@@ -36,11 +52,19 @@
                                 "<link>",
                                 "</link>",
                                 new NoUnderlineClickableSpan(
-                                        context, unused -> settingsLinkCallback.run())));
+                                        context, unused -> mSettingsLinkCallback.run())));
         linkView.setText(linkText);
         linkView.setMovementMethod(LinkMovementMethod.getInstance());
     }
 
+    public void setButtonCallback(Runnable buttonCallback) {
+        mButtonCallback = buttonCallback;
+    }
+
+    public void setSettingsLinkCallback(Runnable settingsLinkCallback) {
+        mSettingsLinkCallback = settingsLinkCallback;
+    }
+
     @Override
     public View getContentView() {
         return mContentView;
diff --git a/chrome/browser/password_manager/android/account_storage_notice/javatests/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorTest.java b/chrome/browser/password_manager/android/account_storage_notice/javatests/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorIntegrationTest.java
similarity index 66%
rename from chrome/browser/password_manager/android/account_storage_notice/javatests/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorTest.java
rename to chrome/browser/password_manager/android/account_storage_notice/javatests/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorIntegrationTest.java
index 80083fd..53933fb 100644
--- a/chrome/browser/password_manager/android/account_storage_notice/javatests/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorTest.java
+++ b/chrome/browser/password_manager/android/account_storage_notice/javatests/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorIntegrationTest.java
@@ -28,20 +28,36 @@
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.preferences.Pref;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
+import org.chromium.chrome.browser.sync.SyncServiceFactory;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.browser.signin.SigninTestRule;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
+import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
-/** Tests for AccountStorageNoticeCoordinator. */
+/**
+ * Tests that verify AccountStorageNoticeCoordinator's interaction with the view, e.g. click
+ * handling. These do not test the logic for when to show the view or not, see
+ * AccountStorageNoticeCoordinatorUnitTest for that. They also do not test the integration with
+ * embedders (saving and filling flows).
+ */
 @Batch(Batch.PER_CLASS)
 @CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
 @RunWith(ChromeJUnit4ClassRunner.class)
-public class AccountStorageNoticeCoordinatorTest {
+@EnableFeatures(ChromeFeatureList.ENABLE_PASSWORDS_ACCOUNT_STORAGE_FOR_NON_SYNCING_USERS)
+public class AccountStorageNoticeCoordinatorIntegrationTest {
+    @Rule public SigninTestRule mSigninTestRule = new SigninTestRule();
+
     @Rule public ChromeTabbedActivityTestRule mActivityRule = new ChromeTabbedActivityTestRule();
 
     @Rule
@@ -62,6 +78,13 @@
     public void setUp() {
         mJniMocker.mock(AccountStorageNoticeCoordinatorJni.TEST_HOOKS, mJniMock);
         mActivityRule.startMainActivityOnBlankPage();
+        mSigninTestRule.addTestAccountThenSignin();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    // Tests are batched, so reset the pref, otherwise the notice only shows once.
+                    UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
+                            .clearPref(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN);
+                });
     }
 
     @Test
@@ -108,12 +131,26 @@
 
     @Test
     @MediumTest
-    public void testDestroyWhileShowing() {
+    public void testHideImmediatelyIfShowing() {
         AccountStorageNoticeCoordinator coordinator = createCoordinator();
         waitSheetVisible(true);
         verify(mJniMock, never()).onClosed(NATIVE_OBSERVER_PTR);
 
-        TestThreadUtils.runOnUiThreadBlocking(() -> coordinator.destroy());
+        TestThreadUtils.runOnUiThreadBlocking(() -> coordinator.hideImmediatelyIfShowing());
+
+        waitSheetVisible(false);
+        verify(mJniMock).onClosed(NATIVE_OBSERVER_PTR);
+    }
+
+    @Test
+    @MediumTest
+    public void testHideWithoutObserver() {
+        AccountStorageNoticeCoordinator coordinator = createCoordinator();
+        waitSheetVisible(true);
+        verify(mJniMock, never()).onClosed(NATIVE_OBSERVER_PTR);
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> coordinator.setObserver(0));
+        TestThreadUtils.runOnUiThreadBlocking(() -> coordinator.hideImmediatelyIfShowing());
 
         waitSheetVisible(false);
         verify(mJniMock, never()).onClosed(NATIVE_OBSERVER_PTR);
@@ -122,10 +159,16 @@
     private AccountStorageNoticeCoordinator createCoordinator() {
         return TestThreadUtils.runOnUiThreadBlockingNoException(
                 () -> {
-                    return new AccountStorageNoticeCoordinator(
-                            mActivityRule.getActivity().getWindowAndroid(),
-                            new SettingsLauncherImpl(),
-                            NATIVE_OBSERVER_PTR);
+                    Profile profile = ProfileManager.getLastUsedRegularProfile();
+                    AccountStorageNoticeCoordinator coordinator =
+                            AccountStorageNoticeCoordinator.create(
+                                    SyncServiceFactory.getForProfile(
+                                            ProfileManager.getLastUsedRegularProfile()),
+                                    UserPrefs.get(profile),
+                                    mActivityRule.getActivity().getWindowAndroid(),
+                                    new SettingsLauncherImpl());
+                    coordinator.setObserver(NATIVE_OBSERVER_PTR);
+                    return coordinator;
                 });
     }
 
diff --git a/chrome/browser/password_manager/android/account_storage_notice/junit/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorUnitTest.java b/chrome/browser/password_manager/android/account_storage_notice/junit/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorUnitTest.java
new file mode 100644
index 0000000..4733f41
--- /dev/null
+++ b/chrome/browser/password_manager/android/account_storage_notice/junit/src/org/chromium/chrome/browser/password_manager/account_storage_notice/AccountStorageNoticeCoordinatorUnitTest.java
@@ -0,0 +1,226 @@
+// 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.
+
+package org.chromium.chrome.browser.password_manager.account_storage_notice;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.DisableFeatures;
+import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.preferences.Pref;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
+import org.chromium.components.browser_ui.settings.SettingsLauncher;
+import org.chromium.components.prefs.PrefService;
+import org.chromium.components.sync.SyncService;
+import org.chromium.components.sync.UserSelectableType;
+import org.chromium.ui.base.WindowAndroid;
+
+import java.lang.ref.WeakReference;
+import java.util.Set;
+
+/**
+ * Unit tests for AccountStorageNoticeCoordinator. These are only meant to test the logic for when
+ * to create the coordinator or not. For anything UI/click related, use the *IntegrationTest.java.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(
+        manifest = Config.NONE,
+        shadows = {
+            AccountStorageNoticeCoordinatorUnitTest.ShadowBottomSheetControllerProvider.class
+        })
+public class AccountStorageNoticeCoordinatorUnitTest {
+    @Implements(BottomSheetControllerProvider.class)
+    public static class ShadowBottomSheetControllerProvider {
+        private static BottomSheetController sBottomSheetController;
+
+        @Implementation
+        public static BottomSheetController from(WindowAndroid windowAndroid) {
+            return sBottomSheetController;
+        }
+
+        public static void setBottomSheetController(BottomSheetController controller) {
+            sBottomSheetController = controller;
+        }
+    }
+
+    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock private BottomSheetController mBottomSheetController;
+    @Mock private SyncService mSyncService;
+    @Mock private PrefService mPrefService;
+    @Mock private WindowAndroid mWindowAndroid;
+    @Mock private Context mContext;
+    @Mock private SettingsLauncher mSettingsLauncher;
+
+    @Before
+    public void setUp() {
+        when(mWindowAndroid.getContext()).thenReturn(new WeakReference(mContext));
+        ShadowBottomSheetControllerProvider.setBottomSheetController(mBottomSheetController);
+        // TODO(crbug.com/341176706): Shadow the AccountStorageNoticeView constructor instead.
+        AccountStorageNoticeView.setSkipLayoutForTesting(true);
+    }
+
+    @Test
+    @SmallTest
+    public void testShouldNotCreateIfIncognito() {
+        when(mPrefService.getBoolean(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN)).thenReturn(false);
+
+        @Nullable
+        AccountStorageNoticeCoordinator coordinator =
+                AccountStorageNoticeCoordinator.create(
+                        /* syncService= */ null, mPrefService, mWindowAndroid, mSettingsLauncher);
+
+        Assert.assertEquals(coordinator, null);
+        verify(mPrefService, never())
+                .setBoolean(eq(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN), anyBoolean());
+    }
+
+    @Test
+    @SmallTest
+    public void testShouldNotCreateIfSyncing() {
+        when(mSyncService.hasSyncConsent()).thenReturn(true);
+        when(mSyncService.getSelectedTypes()).thenReturn(Set.of(UserSelectableType.PASSWORDS));
+        when(mPrefService.getBoolean(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN)).thenReturn(false);
+
+        @Nullable
+        AccountStorageNoticeCoordinator coordinator =
+                AccountStorageNoticeCoordinator.create(
+                        mSyncService, mPrefService, mWindowAndroid, mSettingsLauncher);
+
+        Assert.assertEquals(coordinator, null);
+        verify(mPrefService, never())
+                .setBoolean(eq(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN), anyBoolean());
+    }
+
+    @Test
+    @SmallTest
+    public void testShouldNotCreateIfSignedOut() {
+        when(mSyncService.hasSyncConsent()).thenReturn(false);
+        when(mSyncService.getSelectedTypes()).thenReturn(Set.of());
+        when(mPrefService.getBoolean(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN)).thenReturn(false);
+
+        @Nullable
+        AccountStorageNoticeCoordinator coordinator =
+                AccountStorageNoticeCoordinator.create(
+                        mSyncService, mPrefService, mWindowAndroid, mSettingsLauncher);
+
+        Assert.assertEquals(coordinator, null);
+        verify(mPrefService, never())
+                .setBoolean(eq(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN), anyBoolean());
+    }
+
+    @Test
+    @SmallTest
+    public void testShouldNotCreateIfPasswordsDataTypeDisabled() {
+        when(mSyncService.hasSyncConsent()).thenReturn(false);
+        when(mSyncService.getSelectedTypes()).thenReturn(Set.of());
+        when(mPrefService.getBoolean(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN)).thenReturn(false);
+
+        @Nullable
+        AccountStorageNoticeCoordinator coordinator =
+                AccountStorageNoticeCoordinator.create(
+                        mSyncService, mPrefService, mWindowAndroid, mSettingsLauncher);
+
+        Assert.assertEquals(coordinator, null);
+        verify(mPrefService, never())
+                .setBoolean(eq(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN), anyBoolean());
+    }
+
+    @Test
+    @SmallTest
+    public void testShouldNotCreateIfAlreadyShown() {
+        when(mSyncService.hasSyncConsent()).thenReturn(false);
+        when(mSyncService.getSelectedTypes()).thenReturn(Set.of(UserSelectableType.PASSWORDS));
+        when(mPrefService.getBoolean(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN)).thenReturn(true);
+
+        @Nullable
+        AccountStorageNoticeCoordinator coordinator =
+                AccountStorageNoticeCoordinator.create(
+                        mSyncService, mPrefService, mWindowAndroid, mSettingsLauncher);
+
+        Assert.assertEquals(coordinator, null);
+        verify(mPrefService, never())
+                .setBoolean(eq(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN), anyBoolean());
+    }
+
+    @Test
+    @SmallTest
+    @DisableFeatures(ChromeFeatureList.ENABLE_PASSWORDS_ACCOUNT_STORAGE_FOR_NON_SYNCING_USERS)
+    public void testShouldNotCreateIfFlagDisabled() {
+        when(mSyncService.hasSyncConsent()).thenReturn(false);
+        when(mSyncService.getSelectedTypes()).thenReturn(Set.of(UserSelectableType.PASSWORDS));
+        when(mPrefService.getBoolean(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN)).thenReturn(false);
+
+        @Nullable
+        AccountStorageNoticeCoordinator coordinator =
+                AccountStorageNoticeCoordinator.create(
+                        mSyncService, mPrefService, mWindowAndroid, mSettingsLauncher);
+
+        Assert.assertEquals(coordinator, null);
+        verify(mPrefService, never())
+                .setBoolean(eq(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN), anyBoolean());
+    }
+
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.ENABLE_PASSWORDS_ACCOUNT_STORAGE_FOR_NON_SYNCING_USERS)
+    public void testShouldNotCreateIfRequestShowContentFailed() {
+        when(mSyncService.hasSyncConsent()).thenReturn(false);
+        when(mSyncService.getSelectedTypes()).thenReturn(Set.of(UserSelectableType.PASSWORDS));
+        when(mPrefService.getBoolean(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN)).thenReturn(false);
+        when(mBottomSheetController.requestShowContent(any(), anyBoolean())).thenReturn(false);
+
+        @Nullable
+        AccountStorageNoticeCoordinator coordinator =
+                AccountStorageNoticeCoordinator.create(
+                        mSyncService, mPrefService, mWindowAndroid, mSettingsLauncher);
+
+        Assert.assertEquals(coordinator, null);
+        verify(mPrefService, never())
+                .setBoolean(eq(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN), anyBoolean());
+    }
+
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.ENABLE_PASSWORDS_ACCOUNT_STORAGE_FOR_NON_SYNCING_USERS)
+    public void testShouldCreate() {
+        when(mSyncService.hasSyncConsent()).thenReturn(false);
+        when(mSyncService.getSelectedTypes()).thenReturn(Set.of(UserSelectableType.PASSWORDS));
+        when(mPrefService.getBoolean(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN)).thenReturn(false);
+        when(mBottomSheetController.requestShowContent(any(), anyBoolean())).thenReturn(true);
+
+        @Nullable
+        AccountStorageNoticeCoordinator coordinator =
+                AccountStorageNoticeCoordinator.create(
+                        mSyncService, mPrefService, mWindowAndroid, mSettingsLauncher);
+
+        Assert.assertNotEquals(coordinator, null);
+        verify(mPrefService).setBoolean(Pref.ACCOUNT_STORAGE_NOTICE_SHOWN, true);
+    }
+}
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 5ab957a..ebe3263 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -81,7 +81,6 @@
 #include "components/password_manager/core/browser/password_store/password_store_interface.h"
 #include "components/password_manager/core/browser/password_sync_util.h"
 #include "components/password_manager/core/common/password_manager_features.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/profile_metrics/browser_profile_type.h"
 #include "components/safe_browsing/buildflags.h"
@@ -89,8 +88,6 @@
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/site_isolation/site_isolation_policy.h"
-#include "components/sync/base/features.h"
-#include "components/sync/base/user_selectable_type.h"
 #include "components/sync/service/sync_service.h"
 #include "components/sync/service/sync_user_settings.h"
 #include "components/translate/core/browser/translate_manager.h"
@@ -144,6 +141,7 @@
 #include "components/password_manager/content/browser/keyboard_replacing_surface_visibility_controller_impl.h"
 #include "components/password_manager/core/browser/credential_cache.h"
 #include "components/password_manager/core/browser/password_credential_filler_impl.h"
+#include "components/webauthn/android/webauthn_cred_man_delegate.h"
 #include "components/webauthn/android/webauthn_cred_man_delegate_factory.h"
 #else
 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
@@ -295,9 +293,9 @@
   if (form_to_save->IsBlocklisted())
     return false;
 
-  // base::Unretained() is safe: the callback is only invoked if
-  // `account_storage_notice_` is still alive. Then so is its parent
-  // ChromePasswordManagerClient, and so is web_contents() (the client is per
+  // base::Unretained() is safe: If the callback is called, AccountStorageNotice
+  // is alive, then so are its parent ChromePasswordManagerClient, its sibling
+  // SaveUpdatePasswordMessageDelegate and web_contents() (the client is per
   // web_contents()).
   MaybeShowAccountStorageNotice(base::BindOnce(
       &SaveUpdatePasswordMessageDelegate::DisplaySaveUpdatePasswordPrompt,
@@ -445,15 +443,18 @@
   }
 }
 
-bool ChromePasswordManagerClient::ShowKeyboardReplacingSurface(
+void ChromePasswordManagerClient::ShowKeyboardReplacingSurface(
     password_manager::PasswordManagerDriver* driver,
     const password_manager::PasswordFillingParams& password_filling_params,
-    bool is_webauthn_form) {
+    bool is_webauthn_form,
+    base::OnceCallback<void(bool)> shown_cb) {
   if (base::FeatureList::IsEnabled(
           password_manager::features::kPasswordSuggestionBottomSheetV2) &&
       keyboard_replacing_surface_visibility_controller_ &&
       !keyboard_replacing_surface_visibility_controller_->CanBeShown()) {
-    return keyboard_replacing_surface_visibility_controller_->IsVisible();
+    std::move(shown_cb).Run(
+        keyboard_replacing_surface_visibility_controller_->IsVisible());
+    return;
   }
 
   password_manager::ContentPasswordManagerDriver* content_driver =
@@ -464,8 +465,31 @@
           std::make_unique<password_manager::PasswordCredentialFillerImpl>(
               driver->AsWeakPtr(), password_filling_params),
           content_driver->AsWeakPtrImpl(), is_webauthn_form)) {
-    return true;
+    std::move(shown_cb).Run(true);
+    return;
   }
+
+  // base::Unretained() is safe: if the callback is called, AccountStorageNotice
+  // is alive, then so is its parent ChromePasswordManagerClient.
+  MaybeShowAccountStorageNotice(
+      base::BindOnce(&ChromePasswordManagerClient::
+                         ShowKeyboardReplacingSurfaceOnAccountStorageNoticeDone,
+                     base::Unretained(this), content_driver->AsWeakPtrImpl(),
+                     password_filling_params, std::move(shown_cb)));
+}
+
+void ChromePasswordManagerClient::
+    ShowKeyboardReplacingSurfaceOnAccountStorageNoticeDone(
+        base::WeakPtr<password_manager::ContentPasswordManagerDriver>
+            weak_driver,
+        const password_manager::PasswordFillingParams& password_filling_params,
+        base::OnceCallback<void(bool)> shown_cb) {
+  // TODO(crbug.com/338576301): Maybe don't show TTF if there was a navigation.
+  if (!weak_driver) {
+    return std::move(shown_cb).Run(false);
+  }
+
+  password_manager::ContentPasswordManagerDriver* driver = weak_driver.get();
   auto* webauthn_delegate = GetWebAuthnCredentialsDelegateForDriver(driver);
   std::vector<password_manager::PasskeyCredential> passkeys;
   bool should_show_hybrid_option = false;
@@ -486,16 +510,13 @@
           TouchToFillControllerAutofillDelegate::ShowHybridOption(
               should_show_hybrid_option));
 
-  // TODO(crbug.com/338576301): Call MaybeShowAccountStorageNotice() here too.
-  // Figure out whether the bool must be returned async, or if sync is good
-  // enough.
-  return GetOrCreateTouchToFillController()->Show(
+  const bool shown = GetOrCreateTouchToFillController()->Show(
       credential_cache_
           .GetCredentialStore(URLToOrigin(driver->GetLastCommittedURL()))
           .GetCredentials(),
       passkeys, std::move(ttf_controller_autofill_delegate),
-      GetWebAuthnCredManDelegateForDriver(driver),
-      content_driver->AsWeakPtrImpl());
+      GetWebAuthnCredManDelegateForDriver(driver), driver->AsWeakPtrImpl());
+  std::move(shown_cb).Run(shown);
 }
 #endif
 
@@ -1417,26 +1438,29 @@
 
 void ChromePasswordManagerClient::MaybeShowAccountStorageNotice(
     base::OnceClosure callback) {
-  PrefService* pref_service = profile_->GetPrefs();
-  syncer::SyncService* sync_service =
-      SyncServiceFactory::GetForProfile(profile_);
-  if (!AccountStorageNotice::ShouldShow(pref_service, sync_service)) {
-    std::move(callback).Run();
-    return;
-  }
-
-  // `account_storage_notice_` must be null, since ShouldShow() is true and
-  // this is a one-off notice.
-  CHECK(!account_storage_notice_);
   // Unretained() is safe because `this` outlives `account_storage_notice_`.
   auto destroy_notice_cb = base::BindOnce(
       [](ChromePasswordManagerClient* client) {
         client->account_storage_notice_.reset();
       },
       base::Unretained(this));
-  account_storage_notice_ = std::make_unique<AccountStorageNotice>(
-      web_contents(), pref_service, sync_service,
+  const bool had_notice = account_storage_notice_.get();
+  account_storage_notice_ = AccountStorageNotice::MaybeShow(
+      SyncServiceFactory::GetForProfile(profile_), profile_->GetPrefs(),
+      web_contents()->GetNativeView()->GetWindowAndroid(),
       std::move(destroy_notice_cb).Then(std::move(callback)));
+  // MaybeShow() will return non-null at most once, since this is a one-off
+  // notice. So the possible cases are:
+  // - `account_storage_notice_` was null and stayed so:  No notice shown, just
+  //   invokes `callback`.
+  // - `account_storage_notice_` was null and became non-null: Shows the notice.
+  // - (Speculative) `account_storage_notice_` was non-null and became null:
+  //   Hides the notice and executes `callback`. The alternative would be to
+  //   ignore `callback` and wait for `account_storage_notice_` to go away, but
+  //   that's dangerous (if there's a bug and `account_storage_notice_` is never
+  //   reset, the method would always no-op, breaking the saving/filling
+  //   callers).
+  CHECK(!had_notice || !account_storage_notice_);
 }
 
 password_manager::CredManController*
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.h b/chrome/browser/password_manager/chrome_password_manager_client.h
index 1823e0d1..0781c6a 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.h
+++ b/chrome/browser/password_manager/chrome_password_manager_client.h
@@ -146,10 +146,11 @@
       password_manager::ErrorMessageFlowType flow_type,
       password_manager::PasswordStoreBackendErrorType error_type) override;
 
-  bool ShowKeyboardReplacingSurface(
+  void ShowKeyboardReplacingSurface(
       password_manager::PasswordManagerDriver* driver,
       const password_manager::PasswordFillingParams& password_filling_params,
-      bool is_webauthn_form) override;
+      bool is_webauthn_form,
+      base::OnceCallback<void(bool)> shown_cb) override;
 #endif
 
   bool CanUseBiometricAuthForFilling(
@@ -368,6 +369,11 @@
   TouchToFillController* GetOrCreateTouchToFillController();
 
   void MaybeShowAccountStorageNotice(base::OnceClosure callback);
+
+  void ShowKeyboardReplacingSurfaceOnAccountStorageNoticeDone(
+      base::WeakPtr<password_manager::ContentPasswordManagerDriver> weak_driver,
+      const password_manager::PasswordFillingParams& password_filling_params,
+      base::OnceCallback<void(bool)> shown_cb);
 #endif
 
   // content::WebContentsObserver overrides.
diff --git a/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.html b/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.html
index a2d8080ea..c04a749c 100644
--- a/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.html
+++ b/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.html
@@ -111,7 +111,6 @@
             <template is="dom-if" if="[[androidAppsInfo.playStoreEnabled]]">
               <cr-icon-button class="subpage-arrow"
                   aria-label="$i18n{androidAppsPageTitle}"
-                  aria-describedby="secondaryText"
                   aria-roledescription="$i18n{subpageArrowRoleDescription}">
               </cr-icon-button>
             </template>
diff --git a/chrome/browser/screen_ai/main_content_extraction_browsertest.cc b/chrome/browser/screen_ai/main_content_extraction_browsertest.cc
new file mode 100644
index 0000000..0dcabf5
--- /dev/null
+++ b/chrome/browser/screen_ai/main_content_extraction_browsertest.cc
@@ -0,0 +1,96 @@
+// 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.
+
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_future.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/screen_ai/screen_ai_install_state.h"
+#include "chrome/browser/screen_ai/screen_ai_service_router.h"
+#include "chrome/browser/screen_ai/screen_ai_service_router_factory.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/test/browser_test.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "services/screen_ai/public/cpp/utilities.h"
+#include "services/screen_ai/public/mojom/screen_ai_service.mojom.h"
+#include "ui/accessibility/accessibility_features.h"
+#include "ui/accessibility/ax_features.mojom-features.h"
+
+namespace screen_ai {
+
+class MainContentExtractionTest : public InProcessBrowserTest {
+ public:
+  MainContentExtractionTest() {
+    feature_list_.InitWithFeatures(
+        {
+            features::kScreenAITestMode,
+            features::kReadAnythingWithScreen2x,
+            ax::mojom::features::kScreenAIMainContentExtractionEnabled,
+        },
+        {});
+  }
+
+  ~MainContentExtractionTest() override = default;
+
+  // InProcessBrowserTest:
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+
+    ScreenAIInstallState::GetInstance()->SetComponentFolder(
+        GetComponentBinaryPathForTests().DirName());
+  }
+
+  void Connect() {
+    base::test::TestFuture<bool> future;
+    ScreenAIServiceRouterFactory::GetForBrowserContext(browser()->profile())
+        ->GetServiceStateAsync(
+            ScreenAIServiceRouter::Service::kMainContentExtraction,
+            future.GetCallback());
+    ASSERT_TRUE(future.Wait()) << "Service state callback not called.";
+    ASSERT_TRUE(future.Get<bool>()) << "Service initialization failed.";
+
+    ScreenAIServiceRouterFactory::GetForBrowserContext(browser()->profile())
+        ->BindMainContentExtractor(
+            main_content_extractor_.BindNewPipeAndPassReceiver());
+  }
+
+  void ExtractMainContent(const ui::AXTreeUpdate& ax_tree_update,
+                          std::vector<ui::AXNodeID>& main_content) {
+    base::test::TestFuture<const std::vector<ui::AXNodeID>&> future;
+    main_content_extractor_->ExtractMainContent(
+        ax_tree_update, ukm::kInvalidSourceId, future.GetCallback());
+    ASSERT_TRUE(future.Wait()) << "Main content was not received.";
+    main_content = future.Get();
+  }
+
+ private:
+  mojo::Remote<screen_ai::mojom::Screen2xMainContentExtractor>
+      main_content_extractor_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(MainContentExtractionTest, EmptyInput) {
+  Connect();
+
+  ui::AXNodeData root;
+  root.id = 1;
+  root.role = ax::mojom::Role::kRootWebArea;
+  root.AddBoolAttribute(ax::mojom::BoolAttribute::kNonAtomicTextFieldRoot,
+                        true);
+  root.relative_bounds.bounds = gfx::RectF(0, 0, 640, 480);
+
+  ui::AXTreeUpdate empty_tree;
+  empty_tree.root_id = root.id;
+  empty_tree.nodes = {root};
+
+  std::vector<ui::AXNodeID> main_content;
+  ExtractMainContent(empty_tree, main_content);
+
+  ASSERT_EQ(0u, main_content.size());
+}
+
+// TODO(crbug.com/41489544): Add tests with non-empty inputs and multiple calls.
+
+}  // namespace screen_ai
diff --git a/chrome/browser/ui/android/fast_checkout/fast_checkout_view_impl.cc b/chrome/browser/ui/android/fast_checkout/fast_checkout_view_impl.cc
index e530218..306a0f29 100644
--- a/chrome/browser/ui/android/fast_checkout/fast_checkout_view_impl.cc
+++ b/chrome/browser/ui/android/fast_checkout/fast_checkout_view_impl.cc
@@ -75,7 +75,7 @@
   std::vector<base::android::ScopedJavaLocalRef<jobject>> credit_cards_array;
   credit_cards_array.reserve(credit_cards.size());
   for (const autofill::CreditCard* card : credit_cards) {
-    autofill_profiles_array.push_back(CreateFastCheckoutCreditCard(
+    credit_cards_array.push_back(CreateFastCheckoutCreditCard(
         env, *card, g_browser_process->GetApplicationLocale()));
   }
 
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb
index e25a76d..dac25f0 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_lo.xtb
@@ -244,7 +244,9 @@
 <translation id="2318045970523081853">ແຕະເພື່ອໂທອອກ</translation>
 <translation id="2321086116217818302">ກຳລັງກະກຽມລະຫັດຜ່ານ…</translation>
 <translation id="2323763861024343754">ບ່ອນເກັບຂໍ້ມູນຂອງເວັບໄຊ</translation>
+<translation id="2328739396024849021">ປິດການນຳໃຊ້ປຸ່ມບັນທຶກແລ້ວ</translation>
 <translation id="2328985652426384049">ບໍ່ສາມາດເຂົ້າສູ່ລະບົບໄດ້</translation>
+<translation id="233375395665273385">ລຶບ ແລະ ອອກຈາກລະບົບ</translation>
 <translation id="2341410551640223969">ບໍ່ສາມາດຕິດຕັ້ງ <ph name="WEBAPK_NAME" /> ໄດ້.</translation>
 <translation id="2349710944427398404">ຂໍ້ມູນທັງໝົດທີ່ໃຊ້ໂດຍ Chrome, ລວມທັງບັນຊີ, ບຸກມາກ ແລະ ການຕັ້ງຄ່າທີ່ບັນທຶກໄວ້</translation>
 <translation id="235789365079050412">ນະໂຍບາຍຄວາມເປັນສ່ວນຕົວ Google</translation>
@@ -291,12 +293,14 @@
 <translation id="2571834852878229351">ສ້າງຂອງຂ້ອຍເອງ</translation>
 <translation id="2574249610672786438">ເພື່ອເຫັນແຖບຂອງທ່ານຈາກທຸກບ່ອນທີ່ທ່ານໃຊ້ Chrome, ກະລຸນາເຂົ້າສູ່ລະບົບໃນອຸປະກອນທັງໝົດຂອງທ່ານ</translation>
 <translation id="2578337197553672982">ເນື້ອຫາໂດຍ Google ສຳລັບເດັກນ້ອຍ</translation>
+<translation id="2579297619530305344">ລະຫັດຜ່ານໃນອຸປະກອນທຸກເຄື່ອງຂອງທ່ານເປີດແບບເຕັມໜ້າ</translation>
 <translation id="2581165646603367611">ນີ້ຈະລຶບລ້າງຄຸກກີ້, ແຄສ ແລະ ຂໍ້ມູນອື່ນຂອງເວັບໄຊທີ່ Chrome ຄິດວ່າບໍ່ສຳຄັນ.</translation>
 <translation id="2587052924345400782">ມີເວີຊັນທີ່ໃໝ່ກວ່າ</translation>
 <translation id="2593272815202181319">Monospace</translation>
 <translation id="2603212228005142861">ເຂົ້າສູ່ລະບົບເພື່ອຈັດການການຕັ້ງຄ່າຂອງທ່ານ</translation>
 <translation id="260403163289591229">ກຳລັງຕິດຕາມ</translation>
 <translation id="2604446170045642109">ທ່ານສາມາດປິດຮູບແບບສີສັນມືດສຳລັບເວັບໄຊຕ່າງໆໄດ້ໃນການຕັ້ງຄ່າຂອງທ່ານ.</translation>
+<translation id="2607441479295509868">ອອກຈາກລະບົບແລ້ວ. ເຂົ້າສູ່ລະບົບຄືນໃໝ່ເພື່ອໃຊ້ບຸກມາກ, ລະຫັດຜ່ານ ແລະ ຂໍ້ມູນອື່ນໆໃນບັນຊີຂອງທ່ານ.</translation>
 <translation id="2612676031748830579">ເລກບັດ</translation>
 <translation id="2620314865574742210"><ph name="NAME" /> ໄດ້ເຊີນທ່ານເຂົ້າເຖິງລາຍການທີ່ແບ່ງປັນແລ້ວ.</translation>
 <translation id="2625189173221582860">ສຳເນົາລະຫັດຜ່ານແລ້ວ</translation>
@@ -350,6 +354,7 @@
 <translation id="2869430948265924908">ເພື່ອປົກປ້ອງເນື້ອຫາທີ່ລະອຽດອ່ອນຂອງທ່ານໃນລົດ, ທ່ານຈະຕ້ອງສ້າງການລັອກໂປຣໄຟລ໌ລົດກ່ອນ.  ທ່ານສາມາດເຮັດໄດ້ໂດຍການໃຊ້ PIN, ລະຫັດ ຫຼື ລະຫັດຜ່ານ.</translation>
 <translation id="2870560284913253234">ເວັບ​ໄຊທ໌</translation>
 <translation id="2871733351037274014">ໂຫຼດໜ້າກ່ອນລ່ວງໜ້າ</translation>
+<translation id="2871941920508945325">ປຸ່ມຂໍ້ມູນເຈາະເລິກຂອງໜ້າ</translation>
 <translation id="2876136027428473467"><ph name="CHILD_NAME" /> ຕ້ອງການໃຫ້ທ່ານອະນຸມັດເວັບໄຊນີ້:</translation>
 <translation id="2876628302275096482">ສຶກສາເພີ່ມເຕີມກ່ຽວກັບ <ph name="BEGIN_LINK" />ວິທີທີ່ Chrome ຮັກສາຂໍ້ມູນຂອງທ່ານໃຫ້ເປັນສ່ວນຕົວ<ph name="END_LINK" /></translation>
 <translation id="2888126860611144412">ກ່ຽວ​ກັບ Chrome</translation>
@@ -375,6 +380,7 @@
 <translation id="2951071800649516099">ເພີ່ມໜ້າໃສ່ລາຍຊື່ການອ່ານສຳລັບພາຍຫຼັງ</translation>
 <translation id="2956070106555335453">ສັງລວມ</translation>
 <translation id="2961208450284224863">{READING_LIST_UNREAD_PAGE_COUNT,plural, =1{<ph name="READING_LIST_UNREAD_PAGE_COUNT_ONE" /> ໜ້າທີ່ຍັງບໍ່ໄດ້ອ່ານ}other{<ph name="READING_LIST_UNREAD_PAGE_COUNT_MANY" /> ໜ້າທີ່ຍັງບໍ່ໄດ້ອ່ານ}}</translation>
+<translation id="2976550651269220761">ຂໍ້ມູນ Chrome ບາງສ່ວນຂອງທ່ານຍັງບໍ່ໄດ້ຖືກບັນທຶກໃສ່ໃນບັນຊີ Google ຂອງທ່ານເທື່ອ.\nກະລຸນາລໍຖ້າສອງສາມນາທີກ່ອນອອກຈາກລະບົບ. ຫາກທ່ານອອກຈາກລະບົບຕອນນີ້, ຂໍ້ມູນນີ້ຈະຖືກລຶບ.</translation>
 <translation id="2977350910003566746">ການຈັດຮຽງຕາມເປີດຫຼ້າສຸດ</translation>
 <translation id="297771753501244313">ຈັດຮຽງຕາມເວລາເປີດຫຼ້າສຸດ</translation>
 <translation id="2979025552038692506">ແຖບບໍ່ເປີດເຜີຍຕົວຕົນທີ່ເລືອກໄວ້</translation>
@@ -512,10 +518,13 @@
 <translation id="3692944402865947621">ການດາວໂຫຼດ <ph name="FILE_NAME" /> ບໍ່ສຳເລັດ ເນື່ອງຈາກບໍ່ສາມາດຕິດຕໍ່ຫາບ່ອນຈັດເກັບຂໍ້ມູນໄດ້.</translation>
 <translation id="3697705478071004188">ຮຽງຕາມເວັບໄຊ</translation>
 <translation id="3699022356773522638">ດາວໂຫຼດໄຟລ໌ບໍ?</translation>
+<translation id="3700759344784597882">ເມື່ອເປີດ, ລະບົບຈະບັນທຶກລະຫັດຜ່ານໄວ້ໃນບັນຊີຂອງທ່ານ. ເມື່ອປິດ, ລະບົບຈະບັນທຶກລະຫັດຜ່ານໄວ້ໃນອຸປະກອນນີ້ເທົ່ານັ້ນ.</translation>
 <translation id="3701515417135397388">ເຕືອນທ່ານຫາກລະຫັດຜ່ານຖືກລະເມີດໃນການລະເມີດຂໍ້ມູນ</translation>
+<translation id="3711001040440486668">ປຸ່ມແບ່ງປັນ</translation>
 <translation id="371230970611282515">ຄາດເດົາ ແລະ ເຕືອນທ່ານກ່ຽວກັບເຫດການອັນຕະລາຍກ່ອນທີ່ພວກມັນຈະເກີດຂຶ້ນ.</translation>
 <translation id="3714981814255182093">ເປີດແຖບຊອກຫາ</translation>
 <translation id="3716182511346448902">ໜ້ານີ້ໃຊ້ຄວາມຈຳຫຼາຍເກີນໄປ, ສະນັ້ນ Chrome ໄດ້ຢຸດມັນໄວ້ແລ້ວ.</translation>
+<translation id="3718765429352682176">ທ່ານອາດເຫັນປະຫວັດຈາກແອັບອື່ນໆທີ່ເປີດລິ້ງໃນ Chrome.</translation>
 <translation id="3720422586473670527">ບໍ່, ຂອບໃຈ</translation>
 <translation id="3721119614952978349">ທ່ານ ແລະ Google</translation>
 <translation id="3737319253362202215">ການຕັ້ງຄ່າຄຳແປ</translation>
@@ -523,6 +532,7 @@
 <translation id="3738139272394829648">ສໍາຜັດເພື່ອຄົ້ນຫາ</translation>
 <translation id="3739899004075612870">ເພີ່ມບຸກມາກໃສ່ໃນ <ph name="PRODUCT_NAME" /> ແລ້ວ</translation>
 <translation id="3740525748616366977">ບໍ່ສາມາດໃຊ້ການຊອກຫາດ້ວຍສຽງຢູ່ອຸປະກອນນີ້ໄດ້</translation>
+<translation id="376561056759077985">ຍັງບໍ່ໄດ້ບັນທຶກຂໍ້ມູນບາງຢ່າງ</translation>
 <translation id="3771033907050503522">ແຖບບໍ່ເຜີຍຕົນຕົວ</translation>
 <translation id="3771290962915251154">ການຕັ້ງຄ່ານີ້ຖືກປິດການນຳໃຊ້ເນື່ອງຈາກວ່າການຄວບຄຸມຂອງພໍ່ແມ່ເປີດຢູ່</translation>
 <translation id="3771694256347217732">ຂໍ້ກຳນົດການບໍລິການຂອງ Google</translation>
@@ -703,6 +713,7 @@
 <translation id="4684427112815847243">ຊິງຄ໌​ທຸກ​ຢ່າງ</translation>
 <translation id="4685741273709472646">ເລືອກຈາກລາຍຊື່ແບບເລື່ອນລົງ</translation>
 <translation id="4687718960473379118">ໂຄສະນາທີ່ເວັບໄຊແນະນຳ</translation>
+<translation id="469286762610133730">ຮັບເນື້ອຫາທີ່ດີຂຶ້ນ</translation>
 <translation id="4695891336199304370">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" />\u2026 ແລະ ອີກ <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ທາງເລືອກການຈັດສົ່ງ}other{<ph name="SHIPPING_OPTION_PREVIEW" />\u2026 ແລະ ອີກ <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> ທາງເລືອກການຈັດສົ່ງ}}</translation>
 <translation id="4699172675775169585">ຮູບ ແລະໄຟລ໌ທີ່ແຄຊ໌ແລ້ວ</translation>
 <translation id="4710167854527459075">ຈັດຮຽງຈາກໃໝ່ສຸດ</translation>
@@ -864,6 +875,7 @@
 <translation id="548278423535722844">ເປີດໃນແອັບແຜນທີ່</translation>
 <translation id="5486841595534124508">ໂລໂກ້ຂອງການແຈ້ງເຕືອນຄວາມເປັນສ່ວນຕົວສຳລັບສູນຂໍ້ມູນເຈາະເລິກຂອງໜ້າ ເຊິ່ງປ່ຽນເສັ້ນທາງໄປຫາໜ້າການເຄື່ອນໄຫວຂອງຂ້ອຍ.</translation>
 <translation id="5492637351392383067">ການເຂົ້າລະຫັດຢູ່ອຸປະກອນ</translation>
+<translation id="5503125329065007089">ລະຫັດຜ່ານໃນອຸປະກອນທຸກເຄື່ອງຂອງທ່ານເປີດແບບເຄິ່ງໜ້າ</translation>
 <translation id="5514904542973294328">ປິດນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງອຸປະກອນນີ້</translation>
 <translation id="5515439363601853141">ປົດລັອກເພື່ອເບິ່ງລະຫັດຜ່ານຂອງທ່ານ</translation>
 <translation id="5517095782334947753">ທ່ານມີບຸກມາກ, ປະຫວັດ, ລະຫັດຜ່ານ ແລະ ການຕັ້ງຄ່າອື່ນຈາກ <ph name="FROM_ACCOUNT" />.</translation>
@@ -1018,6 +1030,7 @@
 <translation id="6277522088822131679">ມີບັນຫາການພິມໜ້າ. ກະລຸນາລອງໃໝ່ອີກ.</translation>
 <translation id="6277722725779679269">ບໍ່ສາມາດອັບເດດການຕິດຕາມລາຄາໄດ້</translation>
 <translation id="6278428485366576908">ສີສັນໜ້າຕາ</translation>
+<translation id="6284472661216707937">ປິດລະຫັດຜ່ານໃນອຸປະກອນທຸກເຄື່ອງຂອງທ່ານແລ້ວ</translation>
 <translation id="6295158916970320988">ທຸກ​ເວັບ​ໄຊ​ທ໌</translation>
 <translation id="6303969859164067831">ອອກຈາກລະບົບ ແລະ ປິດການຊິ້ງຂໍ້ມູນ</translation>
 <translation id="6312687380483398334">ແອັບເວັບ (ມິດງຽບ)</translation>
@@ -1093,6 +1106,7 @@
 <translation id="6593061639179217415">ເວັບໄຊສຳລັບເດັສທັອບ</translation>
 <translation id="6594347733515723558">ຕົວເລືອກການຈັດຮຽງ ແລະ ເບິ່ງ</translation>
 <translation id="6595046016124923392">ລະບົບຈະສົ່ງຮູບໃຫ້ Google ເພື່ອປັບປຸງຄຳອະທິບາຍສຳລັບທ່ານ.</translation>
+<translation id="6604931690954120417">ເມື່ອທ່ານເຂົ້າສູ່ລະບົບ Chrome, ລະຫັດຜ່ານທີ່ທ່ານບັນທຶກໄວ້ຈະຢູ່ໃນບັນຊີ Google ຂອງທ່ານ. ເພື່ອປິດຄຸນສົມບັດນີ້ໄວ້, ໃຫ້ <ph name="BEGIN_LINK" />ເຂົ້າໄປການຕັ້ງຄ່າ<ph name="END_LINK" />.</translation>
 <translation id="661266467055912436">ປັບປຸງຄວາມປອດໄພສຳລັບທ່ານ ແລະ ທຸກຄົນໃນເວັບ.</translation>
 <translation id="6621391692573306628">ເພື່ອສົ່ງແຖບນີ້ໄປຫາອຸປະກອນອື່ນ, ໃຫ້ເຂົ້າສູ່ລະບົບຫາ Chrome ຢູ່ທັງສອງອຸປະກອນ</translation>
 <translation id="6625890511281718257">ແບ່ງປັນກັບຊີດຄຳຕິຊົມສະຫຼຸບ</translation>
@@ -1130,6 +1144,7 @@
 <translation id="6751521182688001123">ເປີດແຖບໃໝ່ແບບໄວໆ. ເພື່ອແກ້ໄຂທາງລັດນີ້, ໃຫ້ແຕະຄ້າງໄວ້.</translation>
 <translation id="6756507620369789050">ແບ່ງປັນຄຳຕິຊົມ</translation>
 <translation id="6762511428368667596"><ph name="NAME" />, <ph name="EMAIL" />.</translation>
+<translation id="676305334223455055">ເຂົ້າສູ່ລະບົບເພື່ອເບິ່ງເນື້ອຫາໂດຍອີງຕາມຄວາມສົນໃຈຂອງທ່ານ</translation>
 <translation id="6767294960381293877">ລາຍການອຸປະກອນທີ່ຈະແບ່ງປັນແຖບນຳໂດຍເປີດຢູ່ທີ່ລະດັບລວງສູງເຄິ່ງຈໍ.</translation>
 <translation id="6770602306803890733">ປັບປຸງຄວາມປອດໄພສຳລັບທ່ານ ແລະ ທຸກຄົນໃນເວັບ</translation>
 <translation id="6775840696761158817">ເມື່ອທ່ານແຕະ ຫຼື ພິມໃນແຖບທີ່ຢູ່ ຫຼື ກ່ອງຊອກຫາ, ທ່ານຈະເຫັນຄຳແນະນຳຈາກໂປຣແກຣມຊອກຫາເລີ່ມຕົ້ນຂອງທ່ານ. ສິ່ງນີ້ຖືກປິດໃນໂໝດບໍ່ເປີດເຜີຍຕົວຕົນ.</translation>
@@ -1313,6 +1328,7 @@
 <translation id="757855969265046257">{FILES,plural, =1{ດາວໂຫຼດ <ph name="FILES_DOWNLOADED_ONE" /> ໄຟລ໌ແລ້ວ}other{ດາວໂຫຼດ <ph name="FILES_DOWNLOADED_MANY" /> ໄຟລ໌ແລ້ວ}}</translation>
 <translation id="7581273696622423628">ເຮັດແບບສຳຫຼວດ</translation>
 <translation id="7583262514280211622">ທ່ານຈະເຫັນລາຍການອື່ນຂອງທ່ານຢູ່ບ່ອນນີ້</translation>
+<translation id="758603037873046260">ເຂົ້າສູ່ລະບົບເພື່ອບັນທຶກໜ້ານີ້</translation>
 <translation id="7588219262685291874">ເປີດຮູບແບບສີສັນມືດເມື່ອຕົວປະຢັດແບັດເຕີຣີຂອງອຸປະກອນຂອງທ່ານເປີດຢູ່</translation>
 <translation id="7594687499944811403">ໃຫ້ <ph name="EMBEDDED_ORIGIN" /> ຢັ້ງຢືນວ່າແມ່ນທ່ານສຳລັບ <ph name="TOP_ORIGIN" /></translation>
 <translation id="7596558890252710462">ລະ​ບົບ​ປະຕິບັດການ</translation>
@@ -1353,6 +1369,7 @@
 <translation id="777637629667389858">ປົກປ້ອງທ່ານໃນທົ່ວບໍລິການ Google ເມື່ອທ່ານເຂົ້າສູ່ລະບົບ.</translation>
 <translation id="7778840695157240389">ກວດເບິ່ງເລື່ອງລາວໃໝ່ໃນພາຍຫຼັງ</translation>
 <translation id="7780645209293383778">ລິ້ງຂໍ້ມູນນີ້ກັບບັນຊີ Google ຂອງທ່ານຊົ່ວຄາວເມື່ອທ່ານເຂົ້າສູ່ລະບົບແລ້ວ, ເພື່ອປົກປ້ອງທ່ານໃນຕ່າງໆຂອງ Google</translation>
+<translation id="7786562169000598736">ຮັບບຸກມາກທັງໝົດຂອງທ່ານ</translation>
 <translation id="7791543448312431591">ເພີ່ມ</translation>
 <translation id="7798392620021911922">ກູ້ <ph name="TAB_COUNT" /> ແຖບຄືນແລ້ວ</translation>
 <translation id="780287761701992588">ໃຊ້ບຸກມາກ, ລະຫັດຜ່ານ ແລະ ອື່ນໆຢູ່ອຸປະກອນທັງໝົດຂອງທ່ານ</translation>
@@ -1423,6 +1440,7 @@
 <translation id="8078096376109663956">ແບ່ງປັນຂໍ້ຄວາມເທົ່ານັ້ນ</translation>
 <translation id="8084114998886531721">ບັນທຶກລະຫັດຜ່ານແລ້ວ</translation>
 <translation id="8084285576995584326">ຄວບຄຸມຂໍ້ມູນບັນຊີ Google ຂອງທ່ານ</translation>
+<translation id="8084864785646838999">ປະຫວັດຂອງທ່ານບາງສ່ວນອາດຈະບໍ່ສະແດງຢູ່ບ່ອນນີ້. ເພື່ອເບິ່ງປະຫວັດ Chrome ທັງໝົດຂອງທ່ານ, ໃຫ້ເປີດປະຫວັດ Chrome ທັງໝົດ. ນອກຈາກນີ້, ບັນຊີ Google ຂອງທ່ານອາດມີປະຫວັດການທ່ອງເວັບຮູບແບບອື່ນໆຢູ່ <ph name="BEGIN_LINK" />myactivity.google.com<ph name="END_LINK" />.</translation>
 <translation id="808747664143081553">ເຊື່ອມຕໍ່ກັບອຸປະກອນແລ້ວ</translation>
 <translation id="8088176524274673045">ເພື່ອແບ່ງປັນກັບຄົນທີ່ຢູ່ໃກ້ຄຽງ, ໃຫ້ເຂົາເຈົ້າສະແກນລະຫັດ QR ນີ້ກ່ອນ</translation>
 <translation id="8090732854597034573">ກະລຸນາຂໍຄວາມຊ່ວຍເຫຼືອຈາກຜູ້ປົກຄອງຂອງທ່ານ, ຫາກທ່ານຕ້ອງການ</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ml.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ml.xtb
index 560f6dca..680c18c 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ml.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ml.xtb
@@ -244,7 +244,9 @@
 <translation id="2318045970523081853">കോൾ ചെയ്യാൻ ടാപ്പ് ചെയ്യുക</translation>
 <translation id="2321086116217818302">പാസ്‍വേഡുകൾ തയ്യാറാക്കുന്നു…</translation>
 <translation id="2323763861024343754">സൈറ്റ് സ്‌റ്റോറേജ്</translation>
+<translation id="2328739396024849021">പ്രവർത്തനരഹിതമാക്കിയ 'സംരക്ഷിക്കുക' ബട്ടൺ</translation>
 <translation id="2328985652426384049">സൈൻ ഇൻ ചെയ്യാനാകില്ല</translation>
+<translation id="233375395665273385">ഇല്ലാതാക്കിയ ശേഷം സൈൻ ഔട്ട് ചെയ്യുക</translation>
 <translation id="2341410551640223969"><ph name="WEBAPK_NAME" /> ഇൻസ്‌റ്റാൾ ചെയ്യാനായില്ല.</translation>
 <translation id="2349710944427398404">അക്കൗണ്ടുകളും ബുക്ക്‌മാർക്കുകളും സംരക്ഷിച്ച ക്രമീകരണവും ഉൾപ്പെടെ Chrome ഉപയോഗിക്കുന്ന മൊത്തം വിവരങ്ങൾ</translation>
 <translation id="235789365079050412">Google സ്വകാര്യതാ നയം</translation>
@@ -291,12 +293,14 @@
 <translation id="2571834852878229351">നിങ്ങളുടേതായ പാസ്‌വേഡ് സൃഷ്ടിക്കൂ</translation>
 <translation id="2574249610672786438">Chrome ഉപയോഗിക്കുന്നിടത്തെല്ലാം നിങ്ങളുടെ ടാബുകൾ കാണാൻ, നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങളിലും സൈൻ ഇൻ ചെയ്യുക</translation>
 <translation id="2578337197553672982">പ്രായപൂർത്തിയാകാത്തവർക്കുള്ള Google ഉള്ളടക്കം</translation>
+<translation id="2579297619530305344">നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങളിലെയും പാസ്‌വേഡുകൾ സംബന്ധിച്ച ഷീറ്റ് പൂർണ്ണ വലുപ്പത്തിൽ തുറന്നു</translation>
 <translation id="2581165646603367611">ഇത്, Chrome പ്രധാനമായി കണക്കാക്കാത്ത സൈറ്റുകളുടെ കുക്കികളും കാഷെയും മറ്റ് വിവരങ്ങളും മായ്‌ക്കും.</translation>
 <translation id="2587052924345400782">ഏറ്റവും പുതിയ പതിപ്പ് ലഭ്യമാണ്</translation>
 <translation id="2593272815202181319">മോണോസ്പെ‌യ്‌സ്</translation>
 <translation id="2603212228005142861">മുൻഗണനകൾ മാനേജ് ചെയ്യാൻ സൈൻ ഇൻ ചെയ്യുക</translation>
 <translation id="260403163289591229">ഫോളോ ചെയ്യുന്നത്</translation>
 <translation id="2604446170045642109">സൈറ്റുകൾക്കുള്ള ഡാർക്ക് തീം നിങ്ങളുടെ ക്രമീകരണത്തിൽ ഓഫാക്കാം.</translation>
+<translation id="2607441479295509868">സൈൻ ഔട്ട് ചെയ്‌തു. നിങ്ങളുടെ അക്കൗണ്ടിലെ ബുക്ക്‌മാർക്കുകളും പാസ്‌വേഡുകളും മറ്റും ഉപയോഗിക്കാൻ വീണ്ടും സൈൻ ഇൻ ചെയ്യുക.</translation>
 <translation id="2612676031748830579">കാർഡ് നമ്പർ</translation>
 <translation id="2620314865574742210">പങ്കിട്ട ഇനം ആക്‌സസ് ചെയ്യാൻ, <ph name="NAME" /> നിങ്ങളെ ക്ഷണിച്ചു.</translation>
 <translation id="2625189173221582860">പാസ്‌വേഡ് പകർത്തി</translation>
@@ -350,6 +354,7 @@
 <translation id="2869430948265924908">കാറിലെ നിങ്ങളുടെ സൂക്ഷ്‌മമായി കൈകാര്യം ചെയ്യേണ്ട ഉള്ളടക്കം പരിരക്ഷിക്കാൻ, നിങ്ങളൊരു കാർ പ്രൊഫൈൽ ലോക്ക് സൃഷ്‌ടിക്കണം.  ഒരു പിന്നോ കോഡോ പാസ്‌വേഡോ ഉപയോഗിച്ച് നിങ്ങൾക്ക് ഇത് ചെയ്യാനാകും.</translation>
 <translation id="2870560284913253234">സൈറ്റ്</translation>
 <translation id="2871733351037274014">പേജുകൾ മുൻകൂട്ടി ലോഡ് ചെയ്യുക</translation>
+<translation id="2871941920508945325">പേജ് ഉൾക്കാഴ്‌ച്ചകൾ ബട്ടൺ</translation>
 <translation id="2876136027428473467">ഈ വെബ്സൈറ്റിന് അനുമതി നൽകാൻ <ph name="CHILD_NAME" /> നിങ്ങളോട് ആവശ്യപ്പെടുന്നു:</translation>
 <translation id="2876628302275096482"><ph name="BEGIN_LINK" />Chrome നിങ്ങളുടെ ഡാറ്റ എങ്ങനെ സ്വകാര്യമായി നിലനിർത്തുന്നു<ph name="END_LINK" /> എന്നതിനെക്കുറിച്ച് കൂടുതലറിയുക</translation>
 <translation id="2888126860611144412">Chrome-നെ കുറിച്ച്</translation>
@@ -375,6 +380,7 @@
 <translation id="2951071800649516099">പിന്നീട് വായിക്കാൻ വായിക്കാനുള്ളവയുടെ ലിസ്‌റ്റിലേക്ക് പേജുകൾ ചേർക്കുക</translation>
 <translation id="2956070106555335453">സംഗ്രഹം</translation>
 <translation id="2961208450284224863">{READING_LIST_UNREAD_PAGE_COUNT,plural, =1{വായിക്കാത്ത <ph name="READING_LIST_UNREAD_PAGE_COUNT_ONE" /> പേജ്}other{വായിക്കാത്ത <ph name="READING_LIST_UNREAD_PAGE_COUNT_MANY" /> പേജുകൾ}}</translation>
+<translation id="2976550651269220761">നിങ്ങളുടെ Chrome ഡാറ്റയിൽ ചിലത് നിങ്ങളുടെ Google Account-ൽ ഇപ്പോഴും സംരക്ഷിച്ചിട്ടില്ല.\nഅൽപ്പ സമയം കാത്തിരുന്നതിന് ശേഷം സൈൻ ഔട്ട് ചെയ്യാൻ ശ്രമിക്കുക. നിങ്ങൾ ഇപ്പോൾ സൈൻ ഔട്ട് ചെയ്താൽ ഈ ഡാറ്റ ഇല്ലാതാക്കും.</translation>
 <translation id="2977350910003566746">അവസാനം തുറന്നത് പ്രകാരം അടുക്കുന്നു</translation>
 <translation id="297771753501244313">അവസാനം തുറന്നതനുസരിച്ച് അടുക്കൂ</translation>
 <translation id="2979025552038692506">തിരഞ്ഞെടുത്ത അദൃശ്യ ടാബ്</translation>
@@ -512,10 +518,13 @@
 <translation id="3692944402865947621">സ്റ്റോറേജ് ലൊക്കേഷൻ കണ്ടെത്താനാകാത്തതിനാൽ <ph name="FILE_NAME" /> ഡൗൺലോഡ് ചെയ്യുന്നത് പരാജയപ്പെട്ടു.</translation>
 <translation id="3697705478071004188">സൈറ്റ് അനുസരിച്ച് അടുക്കുക</translation>
 <translation id="3699022356773522638">ഫയൽ ഡൗൺലോഡ് ചെയ്യണോ?</translation>
+<translation id="3700759344784597882">ഓണായിരിക്കുമ്പോൾ, പാസ്‌വേഡുകൾ നിങ്ങളുടെ അക്കൗണ്ടിൽ സംരക്ഷിക്കുന്നു. ഓഫായിരിക്കുമ്പോൾ, ഈ ഉപകരണത്തിൽ മാത്രമേ പാസ്‌വേഡുകൾ സംരക്ഷിക്കൂ.</translation>
 <translation id="3701515417135397388">ഡാറ്റാ ലംഘനത്തിൽ പാസ്‌വേഡ് അപഹരിക്കപ്പെട്ടാൽ നിങ്ങൾക്ക് മുന്നറിയിപ്പ് നൽകുക</translation>
+<translation id="3711001040440486668">പങ്കിടുക ബട്ടൺ</translation>
 <translation id="371230970611282515">അപകടകരമായ ഇവന്റുകൾ നടക്കുന്നതിന് മുമ്പ് അവ പ്രവചിക്കുകയും നിങ്ങൾക്ക് മുന്നറിയിപ്പ് നൽകുകയും ചെയ്യുന്നു.</translation>
 <translation id="3714981814255182093">കണ്ടെത്തൽ ബാർ തുറക്കുക</translation>
 <translation id="3716182511346448902">ഈ പേജ് ഒരുപാട് മെമ്മറി ഉപയോഗിക്കുന്നു, അതിനാൽ Chrome ഇത് താൽക്കാലികമായി അവസാനിപ്പിച്ചു.</translation>
+<translation id="3718765429352682176">Chrome-ൽ ലിങ്കുകൾ തുറക്കുന്ന മറ്റ് ആപ്പുകളിൽ നിന്നുള്ള ചരിത്രവും നിങ്ങൾ കണ്ടേക്കാം.</translation>
 <translation id="3720422586473670527">വേണ്ട</translation>
 <translation id="3721119614952978349">നിങ്ങളും Google-ഉം</translation>
 <translation id="3737319253362202215">വിവർത്തന ക്രമീകരണം</translation>
@@ -523,6 +532,7 @@
 <translation id="3738139272394829648">തിരയാൻ സ്‌പർശിക്കുക</translation>
 <translation id="3739899004075612870"><ph name="PRODUCT_NAME" /> എന്നതിൽ ബുക്ക്‌മാർക്ക് ചെയ്‌തു</translation>
 <translation id="3740525748616366977">ഈ ഉപകരണത്തിൽ ശബ്ദ തിരയൽ ലഭ്യമല്ല</translation>
+<translation id="376561056759077985">ചില ഡാറ്റ ഇതുവരെ സംരക്ഷിച്ചിട്ടില്ല</translation>
 <translation id="3771033907050503522">ആൾമാറാട്ട ടാബുകൾ</translation>
 <translation id="3771290962915251154">രക്ഷാകർതൃ നിയന്ത്രണങ്ങൾ ഓണായതിനാൽ ഈ ക്രമീകരണം പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു</translation>
 <translation id="3771694256347217732">Google സേവന നിബന്ധനകൾ</translation>
@@ -703,6 +713,7 @@
 <translation id="4684427112815847243">എല്ലാം സമന്വയിപ്പിക്കുക</translation>
 <translation id="4685741273709472646">ഡ്രോപ്പ്ഡൗൺ ലിസ്റ്റിൽ നിന്ന് തിരഞ്ഞെടുക്കുക</translation>
 <translation id="4687718960473379118">സൈറ്റ് നിർദ്ദേശിത പരസ്യം</translation>
+<translation id="469286762610133730">കൂടുതൽ മെച്ചപ്പെട്ട ഉള്ളടക്കം നേടുക</translation>
 <translation id="4695891336199304370">{SHIPPING_OPTIONS,plural, =1{<ph name="SHIPPING_OPTION_PREVIEW" />\u2026 എന്നതും ‌മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> പേയ്‌മെന്റ് രീതികളും}other{<ph name="SHIPPING_OPTION_PREVIEW" />\u2026 എന്നതും ‌മറ്റ് <ph name="NUMBER_OF_ADDITIONAL_SHIPPING_OPTIONS" /> പേയ്‌മെന്റ് രീതികളും}}</translation>
 <translation id="4699172675775169585">കാഷെ ചെയ്‌ത ചിത്രങ്ങളും ഫയലുകളും</translation>
 <translation id="4710167854527459075">ഏറ്റവും പുതിയത് ആദ്യം</translation>
@@ -864,6 +875,7 @@
 <translation id="548278423535722844">മാപ്‌സ് ആപ്പിൽ തുറക്കുക</translation>
 <translation id="5486841595534124508">"എന്റെ ആക്റ്റിവിറ്റി" പേജിലേക്ക് റീഡയറക്റ്റ് ചെയ്യുന്ന, പേജ് ഉൾക്കാഴ്ചാ ഹബിനുള്ള സ്വകാര്യതാ അറിയിപ്പിന്റെ ലോഗോ.</translation>
 <translation id="5492637351392383067">ഉപകരണത്തിലെ എൻക്രിപ്ഷൻ</translation>
+<translation id="5503125329065007089">നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങളിലെയും പാസ്‌വേഡുകൾ സംബന്ധിച്ച ഷീറ്റ് പകുതി വലുപ്പത്തിൽ തുറന്നു</translation>
 <translation id="5514904542973294328">ഈ ഉപകരണത്തിന്റെ അഡ്‌മിൻ പ്രവർത്തനരഹിതമാക്കി</translation>
 <translation id="5515439363601853141">നിങ്ങളുടെ പാസ്‍വേഡ് കാണാൻ അൺലോക്ക് ചെയ്യുക</translation>
 <translation id="5517095782334947753">നിങ്ങൾക്ക് <ph name="FROM_ACCOUNT" /> എന്നയാളിൽ നിന്നുള്ള ബുക്ക്‌മാർക്കുകളും ചരിത്രവും പാസ്‌വേഡുകളും മറ്റ് ക്രമീകരണവുമുണ്ട്.</translation>
@@ -1018,6 +1030,7 @@
 <translation id="6277522088822131679">പേജ് പ്രിന്റുചെയ്യുന്നതിൽ ഒരു പ്രശ്‌നമുണ്ടായി. വീണ്ടും ശ്രമിക്കുക.</translation>
 <translation id="6277722725779679269">നിരക്ക് ട്രാക്ക് ചെയ്യൽ അപ്ഡേറ്റ് ചെയ്യാനായില്ല</translation>
 <translation id="6278428485366576908">തീം</translation>
+<translation id="6284472661216707937">'നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങളിലെയും പാസ്‌വേഡുകൾ' സംബന്ധിച്ച ഷീറ്റ് അടച്ചു</translation>
 <translation id="6295158916970320988">എല്ലാ സൈറ്റുകളും</translation>
 <translation id="6303969859164067831">സൈൻ ഔട്ട് ചെയ്‌ത് സമന്വയം ഓഫാക്കുക</translation>
 <translation id="6312687380483398334">വെബ്‌ ആപ്പുകൾ (നിശബ്‌ദം)</translation>
@@ -1093,6 +1106,7 @@
 <translation id="6593061639179217415">ഡെസ്‌ക്‌ടോപ്പ് സൈറ്റ്</translation>
 <translation id="6594347733515723558">ഓപ്ഷനുകൾ അടുക്കുക, കാണുക</translation>
 <translation id="6595046016124923392">നിങ്ങൾ‌ക്കായി വിവരണങ്ങൾ മെച്ചപ്പെടുത്തുന്നതിന് ചിത്രങ്ങൾ Google-ലേക്ക് അയയ്ക്കുന്നു.</translation>
+<translation id="6604931690954120417">Chrome-ൽ സൈൻ ഇൻ ചെയ്‌തിരിക്കുമ്പോൾ നിങ്ങൾ സംരക്ഷിക്കുന്ന പാസ്‍വേഡുകൾ നിങ്ങളുടെ Google Account-ലേക്ക് പോകും. ഇത് ഓഫാക്കാൻ <ph name="BEGIN_LINK" />ക്രമീകരണത്തിലേക്ക് പോകുക<ph name="END_LINK" />.</translation>
 <translation id="661266467055912436">നിങ്ങൾക്കും വെബിലെ എല്ലാവർക്കും സുരക്ഷ മെച്ചപ്പെടുത്തുന്നു.</translation>
 <translation id="6621391692573306628">ഈ ടാബ് മറ്റൊരു ഉപകരണത്തിലേക്ക് അയയ്‌ക്കാൻ, രണ്ട് ഉപകരണങ്ങളിലെയും Chrome-ൽ സൈൻ ഇൻ ചെയ്യുക</translation>
 <translation id="6625890511281718257">'സംഗ്രഹത്തെ കുറിച്ചുള്ള ഫീഡ്ബാക്ക് പങ്കിടുക' ഷീറ്റ്</translation>
@@ -1130,6 +1144,7 @@
 <translation id="6751521182688001123">പുതിയ ടാബ് വേഗത്തിൽ തുറക്കുക. ഈ കുറുക്കുവഴി എഡിറ്റ് ചെയ്യാൻ, സ്‌പർശിച്ചുപിടിക്കുക.</translation>
 <translation id="6756507620369789050">ഫീഡ്ബാക്ക് പങ്കിടുക</translation>
 <translation id="6762511428368667596"><ph name="NAME" />, <ph name="EMAIL" />.</translation>
+<translation id="676305334223455055">നിങ്ങളുടെ താൽപ്പര്യങ്ങളെ അടിസ്ഥാനമാക്കിയുള്ള ഉള്ളടക്കം നേടാൻ സൈൻ ഇൻ ചെയ്യുക</translation>
 <translation id="6767294960381293877">പകുതി ഉയരത്തിൽ ടാബ് പങ്കിടാനാകുന്ന ഉപകരണങ്ങളുടെ ലിസ്‌റ്റ്.</translation>
 <translation id="6770602306803890733">നിങ്ങൾക്കും വെബിലെ എല്ലാവർക്കും സുരക്ഷ മെച്ചപ്പെടുത്തുന്നു</translation>
 <translation id="6775840696761158817">വിലാസ ബാറിലോ തിരയൽ ബോക്‌സിലോ നിങ്ങൾ ടാപ്പ് ചെയ്യുമ്പോഴോ ടൈപ്പ് ചെയ്യുമ്പോഴോ, നിങ്ങളുടെ ഡിഫോൾട്ട് തിരയൽ യന്ത്രത്തിൽ നിന്നുള്ള നിർദ്ദേശങ്ങൾ കാണാം. ഇത് അദൃശ്യ മോഡിൽ ഓഫാണ്.</translation>
@@ -1313,6 +1328,7 @@
 <translation id="757855969265046257">{FILES,plural, =1{<ph name="FILES_DOWNLOADED_ONE" /> ഫയൽ ഡൗൺലോഡ് ചെയ്‌തു}other{<ph name="FILES_DOWNLOADED_MANY" /> ഫയലുകൾ ഡൗൺലോഡ് ചെയ്‌തു}}</translation>
 <translation id="7581273696622423628">സർവേയിൽ പങ്കെടുക്കുക</translation>
 <translation id="7583262514280211622">നിങ്ങളുടെ വായനാ ലിസ്‌റ്റ് ഇവിടെ കാണാം</translation>
+<translation id="758603037873046260">ഈ പേജ് സംരക്ഷിക്കാൻ സൈൻ ഇൻ ചെയ്യുക</translation>
 <translation id="7588219262685291874">ഉപകരണത്തിന്റെ ബാറ്ററി ലാഭിക്കൽ ഓണായിരിക്കുമ്പോൾ ഡാർക്ക് തീം ഓണാക്കുക</translation>
 <translation id="7594687499944811403"><ph name="TOP_ORIGIN" /> ഉപയോഗിക്കുന്നതിന് ഇത് നിങ്ങൾ തന്നെയാണെന്ന് പരിശോധിച്ചുറപ്പിക്കാൻ <ph name="EMBEDDED_ORIGIN" /> -നെ അനുവദിക്കുക</translation>
 <translation id="7596558890252710462">ഓപ്പറേറ്റിംഗ് സിസ്റ്റം</translation>
@@ -1353,6 +1369,7 @@
 <translation id="777637629667389858">സൈൻ ഇൻ ചെയ്തിരിക്കുമ്പോൾ, Google സേവനങ്ങളിലുടനീളം നിങ്ങളെ പരിരക്ഷിക്കുന്നു.</translation>
 <translation id="7778840695157240389">പുതിയ സ്‌റ്റോറികൾക്ക് പിന്നീട് വീണ്ടും പരിശോധിക്കൂ</translation>
 <translation id="7780645209293383778">Google ആപ്പുകളിലുടനീളം പരിരക്ഷിക്കുന്നതിന്, നിങ്ങൾ സൈൻ ഇൻ ചെയ്തിരിക്കുമ്പോൾ ഈ ഡാറ്റ നിങ്ങളുടെ Google അക്കൗണ്ടിലേക്ക് താൽക്കാലികമായി ലിങ്ക് ചെയ്യുന്നു</translation>
+<translation id="7786562169000598736">നിങ്ങളുടെ എല്ലാ ബുക്ക്‌മാർക്കുകളും നേടുക</translation>
 <translation id="7791543448312431591">ചേര്‍ക്കൂ</translation>
 <translation id="7798392620021911922"><ph name="TAB_COUNT" /> ടാബുകൾ പുനഃസ്ഥാപിച്ചു</translation>
 <translation id="780287761701992588">നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങളിലും ബുക്ക്‌മാർക്കുകൾ, പാസ്‌വേഡുകൾ എന്നിവയും മറ്റും നേടുക</translation>
@@ -1423,6 +1440,7 @@
 <translation id="8078096376109663956">ടെക്‌സ്റ്റ് മാത്രം പങ്കിടുക</translation>
 <translation id="8084114998886531721">സംരക്ഷിച്ച പാസ്‌വേഡ്</translation>
 <translation id="8084285576995584326">നിങ്ങളുടെ Google Account ഡാറ്റ നിയന്ത്രിക്കുക</translation>
+<translation id="8084864785646838999">നിങ്ങളുടെ ചരിത്രത്തിൽ ചിലത് ഇവിടെ ദൃശ്യമായേക്കില്ല. നിങ്ങളുടെ പൂർണ്ണ Chrome ചരിത്രം കാണാൻ, പൂർണ്ണ Chrome ചരിത്രം തുറക്കുക. കൂടാതെ, നിങ്ങളുടെ Google Account-ന് <ph name="BEGIN_LINK" />myactivity.google.com<ph name="END_LINK" /> എന്നതിൽ മറ്റ് തരത്തിലുള്ള ബ്രൗസിംഗ് ചരിത്രവും ഉണ്ടായിരിക്കാം.</translation>
 <translation id="808747664143081553">ഉപകരണത്തിലേക്ക് കണക്റ്റ് ചെയ്‌തു</translation>
 <translation id="8088176524274673045">സമീപമുള്ള ആളുകളുമായി പങ്കിടുന്നതിന്, അവരെ ഈ QR കോഡ് സ്‌കാൻ ചെയ്യാൻ അനുവദിക്കുക</translation>
 <translation id="8090732854597034573">നിങ്ങൾക്ക് സഹായം ആവശ്യമുണ്ടെങ്കിൽ രക്ഷിതാവിനോട് ചോദിക്കുക</translation>
diff --git a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc
index 81c378b..5834d6d 100644
--- a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc
+++ b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc
@@ -16,6 +16,7 @@
 #include "components/autofill/content/browser/content_autofill_client.h"
 #include "components/autofill/core/browser/autofill_progress_dialog_type.h"
 #include "components/autofill/core/browser/metrics/payments/payments_window_metrics.h"
+#include "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
 #include "components/autofill/core/browser/payments/payments_requests/unmask_card_request.h"
 #include "components/autofill/core/browser/payments/payments_util.h"
@@ -60,6 +61,22 @@
   CHECK_EQ(flow_type_, FlowType::kNoFlow);
   CHECK_EQ(context.card.record_type(), CreditCard::RecordType::kVirtualCard);
   CHECK(!context.completion_callback.is_null());
+
+  // The VCN 3DS metadata fields are returned from the Payments server. They
+  // must always be present, so that Chrome knows what params to look for on
+  // navigation. Since they are outside of Chrome's control, unexpected values
+  // must be gracefully handled by displaying an error dialog.
+  if (const std::optional<Vcn3dsChallengeOptionMetadata>& metadata =
+          context.challenge_option.vcn_3ds_metadata;
+      !metadata.has_value() || metadata->url_to_open.is_empty() ||
+      metadata->success_query_param_name.empty() ||
+      metadata->failure_query_param_name.empty()) {
+    client_->GetPaymentsAutofillClient()->ShowAutofillErrorDialog(
+        AutofillErrorDialogContext::WithVirtualCardPermanentOrTemporaryError(
+            /*is_permanent_error=*/false));
+    return;
+  }
+
   flow_type_ = FlowType::kVcn3ds;
   vcn_3ds_context_ = std::move(context);
   autofill_metrics::LogVcn3dsFlowEvent(
@@ -71,8 +88,9 @@
         Vcn3dsFlowEvent::kUserConsentDialogSkipped,
         /*user_consent_already_given=*/vcn_3ds_context_
             ->user_consent_already_given);
-    CreatePopup(vcn_3ds_context_->challenge_option.url_to_open,
-                GetPopupSizeForVcn3ds());
+    CreatePopup(
+        vcn_3ds_context_->challenge_option.vcn_3ds_metadata->url_to_open,
+        GetPopupSizeForVcn3ds());
   } else {
     ShowVcn3dsConsentDialog();
   }
@@ -142,9 +160,11 @@
 }
 
 void DesktopPaymentsWindowManager::OnDidFinishNavigationForVcn3ds() {
-  base::expected<RedirectCompletionProof,
+  base::expected<RedirectCompletionResult,
                  Vcn3dsAuthenticationPopupNonSuccessResult>
-      result = ParseUrlForVcn3ds(web_contents()->GetVisibleURL());
+      result = ParseUrlForVcn3ds(
+          web_contents()->GetVisibleURL(),
+          vcn_3ds_context_->challenge_option.vcn_3ds_metadata.value());
   if (result.has_value() ||
       result.error() ==
           Vcn3dsAuthenticationPopupNonSuccessResult::kAuthenticationFailed) {
@@ -160,9 +180,11 @@
 
 void DesktopPaymentsWindowManager::OnWebContentsDestroyedForVcn3ds() {
   CHECK(vcn_3ds_popup_shown_timestamp_.has_value());
-  base::expected<RedirectCompletionProof,
+  base::expected<RedirectCompletionResult,
                  Vcn3dsAuthenticationPopupNonSuccessResult>
-      result = ParseUrlForVcn3ds(web_contents()->GetVisibleURL());
+      result = ParseUrlForVcn3ds(
+          web_contents()->GetVisibleURL(),
+          vcn_3ds_context_->challenge_option.vcn_3ds_metadata.value());
 
   // If the result implies that the authentication inside of the pop-up was
   // successful, continue the flow without resetting.
@@ -219,13 +241,13 @@
 }
 
 void DesktopPaymentsWindowManager::OnDidLoadRiskDataForVcn3ds(
-    RedirectCompletionProof redirect_completion_proof,
+    RedirectCompletionResult redirect_completion_result,
     const std::string& risk_data) {
   client_->GetPaymentsAutofillClient()
       ->GetPaymentsNetworkInterface()
       ->UnmaskCard(CreateUnmaskRequestDetailsForVcn3ds(
                        *client_, vcn_3ds_context_.value(),
-                       std::move(redirect_completion_proof)),
+                       std::move(redirect_completion_result)),
                    base::BindOnce(&DesktopPaymentsWindowManager::
                                       OnVcn3dsAuthenticationResponseReceived,
                                   weak_ptr_factory_.GetWeakPtr()));
@@ -293,7 +315,7 @@
       Vcn3dsFlowEvent::kUserConsentDialogAccepted,
       /*user_consent_already_given=*/vcn_3ds_context_
           ->user_consent_already_given);
-  CreatePopup(vcn_3ds_context_->challenge_option.url_to_open,
+  CreatePopup(vcn_3ds_context_->challenge_option.vcn_3ds_metadata->url_to_open,
               GetPopupSizeForVcn3ds());
 }
 
diff --git a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.h b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.h
index 9e7c28a..7b74e4ea 100644
--- a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.h
+++ b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.h
@@ -89,7 +89,7 @@
   // retrieve the virtual card. This method is run once risk data is loaded for
   // VCN 3DS.
   void OnDidLoadRiskDataForVcn3ds(
-      RedirectCompletionProof redirect_completion_proof,
+      RedirectCompletionResult redirect_completion_result,
       const std::string& risk_data);
 
   // Closes the progress dialog and runs the completion callback
diff --git a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc
index 783e888..0b37d95 100644
--- a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc
+++ b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc
@@ -24,6 +24,7 @@
 #include "components/autofill/content/browser/test_content_autofill_client.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/metrics/payments/payments_window_metrics.h"
+#include "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
 #include "components/autofill/core/browser/payments/payments_window_manager.h"
 #include "components/autofill/core/browser/payments/test_payments_autofill_client.h"
@@ -76,10 +77,13 @@
       client()->set_last_committed_primary_main_frame_url(GURL(kVcn3dsTestUrl));
 
       PaymentsWindowManager::Vcn3dsContext context;
-      card_ = test::GetVirtualCard();
-      context.card = card_;
+      context.card = test::GetVirtualCard();
       context.context_token = kTestContextToken;
-      context.challenge_option.url_to_open = GURL(kVcn3dsTestUrl);
+      Vcn3dsChallengeOptionMetadata metadata;
+      metadata.url_to_open = GURL(kVcn3dsTestUrl);
+      metadata.success_query_param_name = "token";
+      metadata.failure_query_param_name = "failure";
+      context.challenge_option.vcn_3ds_metadata = std::move(metadata);
       context.completion_callback = authentication_complete_callback_.Get();
       context.user_consent_already_given =
           name.find("ConsentAlreadyGiven") != std::string::npos;
@@ -173,20 +177,87 @@
     return authentication_response_;
   }
 
-  CreditCard card_;
   base::HistogramTester histogram_tester_;
+  base::MockCallback<
+      PaymentsWindowManager::OnVcn3dsAuthenticationCompleteCallback>
+      authentication_complete_callback_;
 
  private:
   TestAutofillClientInjector<TestContentAutofillClientForWindowManagerTest>
       test_autofill_client_injector_;
 
-  base::MockCallback<
-      PaymentsWindowManager::OnVcn3dsAuthenticationCompleteCallback>
-      authentication_complete_callback_;
   std::optional<PaymentsWindowManager::Vcn3dsAuthenticationResponse>
       authentication_response_;
 };
 
+// Tests that an error dialog is shown if there is no metadata returned from the
+// server.
+IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest,
+                       InvokeUi_EmptyMetadata_ErrorDialogShown) {
+  PaymentsWindowManager::Vcn3dsContext context;
+  context.card = test::GetVirtualCard();
+  context.context_token = kTestContextToken;
+  context.completion_callback = authentication_complete_callback_.Get();
+  context.user_consent_already_given = true;
+  window_manager().InitVcn3dsAuthentication(std::move(context));
+  EXPECT_TRUE(
+      client()->GetPaymentsAutofillClient()->autofill_error_dialog_shown());
+}
+
+// Tests that an error dialog is shown if there is no URL to open returned from
+// the server.
+IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest,
+                       InvokeUi_EmptyUrlToOpen_ErrorDialogShown) {
+  PaymentsWindowManager::Vcn3dsContext context;
+  context.card = test::GetVirtualCard();
+  context.context_token = kTestContextToken;
+  context.completion_callback = authentication_complete_callback_.Get();
+  context.user_consent_already_given = true;
+  Vcn3dsChallengeOptionMetadata metadata;
+  metadata.success_query_param_name = "token";
+  metadata.failure_query_param_name = "failure";
+  context.challenge_option.vcn_3ds_metadata = std::move(metadata);
+  window_manager().InitVcn3dsAuthentication(std::move(context));
+  EXPECT_TRUE(
+      client()->GetPaymentsAutofillClient()->autofill_error_dialog_shown());
+}
+
+// Tests that an error dialog is shown if there is no success query param name
+// returned from the server.
+IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest,
+                       InvokeUi_EmptySuccessQueryParamName_ErrorDialogShown) {
+  PaymentsWindowManager::Vcn3dsContext context;
+  context.card = test::GetVirtualCard();
+  context.context_token = kTestContextToken;
+  context.completion_callback = authentication_complete_callback_.Get();
+  context.user_consent_already_given = true;
+  Vcn3dsChallengeOptionMetadata metadata;
+  metadata.url_to_open = GURL(kVcn3dsTestUrl);
+  metadata.failure_query_param_name = "failure";
+  context.challenge_option.vcn_3ds_metadata = std::move(metadata);
+  window_manager().InitVcn3dsAuthentication(std::move(context));
+  EXPECT_TRUE(
+      client()->GetPaymentsAutofillClient()->autofill_error_dialog_shown());
+}
+
+// Tests that an error dialog is shown if there is no failure query param name
+// returned from the server.
+IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest,
+                       InvokeUi_EmptyFailureQueryParamName_ErrorDialogShown) {
+  PaymentsWindowManager::Vcn3dsContext context;
+  context.card = test::GetVirtualCard();
+  context.context_token = kTestContextToken;
+  context.completion_callback = authentication_complete_callback_.Get();
+  context.user_consent_already_given = true;
+  Vcn3dsChallengeOptionMetadata metadata;
+  metadata.url_to_open = GURL(kVcn3dsTestUrl);
+  metadata.success_query_param_name = "token";
+  context.challenge_option.vcn_3ds_metadata = std::move(metadata);
+  window_manager().InitVcn3dsAuthentication(std::move(context));
+  EXPECT_TRUE(
+      client()->GetPaymentsAutofillClient()->autofill_error_dialog_shown());
+}
+
 // Test that the VCN 3DS flow started and consent dialog skipped histogram
 // buckets are logged to when the flow starts.
 IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest,
@@ -216,13 +287,13 @@
   ShowUi("Vcn3ds_ConsentAlreadyGiven");
   EXPECT_TRUE(VerifyUi());
 
-  // Navigate to a page where there are shouldProceed and token query params.
+  // Navigate to a page where there is a token query param.
   GetPopupWebContents()->OpenURL(
-      content::OpenURLParams(
-          GURL("https://site.example/?shouldProceed=true&token=sometesttoken"),
-          content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
-          ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
-          /*is_renderer_initiated=*/false),
+      content::OpenURLParams(GURL("https://site.example/?token=sometesttoken"),
+                             content::Referrer(),
+                             WindowOpenDisposition::CURRENT_TAB,
+                             ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
+                             /*is_renderer_initiated=*/false),
       /*navigation_handle_callback=*/{});
 
   base::RunLoop().RunUntilIdle();
@@ -239,8 +310,10 @@
                                ->GetPaymentsNetworkInterface())
                            ->unmask_request();
   ASSERT_TRUE(unmask_request.has_value());
-  EXPECT_EQ(unmask_request->card, card_);
-  EXPECT_EQ(unmask_request->redirect_completion_proof.value(), "sometesttoken");
+  EXPECT_EQ(unmask_request->card,
+            test_api(window_manager()).GetVcn3dsContext()->card);
+  EXPECT_EQ(unmask_request->redirect_completion_result.value(),
+            "sometesttoken");
   EXPECT_EQ(unmask_request->last_committed_primary_main_frame_origin,
             client()->GetLastCommittedPrimaryMainFrameOrigin().GetURL());
 
@@ -257,8 +330,9 @@
 
   EXPECT_EQ(unmask_request->context_token, kTestContextToken);
   ASSERT_TRUE(unmask_request->selected_challenge_option.has_value());
-  EXPECT_EQ(unmask_request->selected_challenge_option->url_to_open,
-            kVcn3dsTestUrl);
+  EXPECT_EQ(
+      unmask_request->selected_challenge_option->vcn_3ds_metadata->url_to_open,
+      kVcn3dsTestUrl);
   std::optional<PaymentsWindowManager::Vcn3dsAuthenticationResponse> response =
       authentication_response();
   ASSERT_TRUE(response.has_value());
@@ -287,13 +361,13 @@
   ShowUi("Vcn3ds_ConsentAlreadyGiven");
   EXPECT_TRUE(VerifyUi());
 
-  // Navigate to a page where there are shouldProceed and token query params.
+  // Navigate to a page where there is a token query param.
   GetPopupWebContents()->OpenURL(
-      content::OpenURLParams(
-          GURL("https://site.example/?shouldProceed=true&token=sometesttoken"),
-          content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
-          ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
-          /*is_renderer_initiated=*/false),
+      content::OpenURLParams(GURL("https://site.example/?token=sometesttoken"),
+                             content::Referrer(),
+                             WindowOpenDisposition::CURRENT_TAB,
+                             ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
+                             /*is_renderer_initiated=*/false),
       /*navigation_handle_callback=*/{});
 
   base::RunLoop().RunUntilIdle();
@@ -327,11 +401,11 @@
 
   // Navigate to a page where there are shouldProceed and token query params.
   GetPopupWebContents()->OpenURL(
-      content::OpenURLParams(
-          GURL("https://site.example/?shouldProceed=true&token=sometesttoken"),
-          content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
-          ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
-          /*is_renderer_initiated=*/false),
+      content::OpenURLParams(GURL("https://site.example/?token=sometesttoken"),
+                             content::Referrer(),
+                             WindowOpenDisposition::CURRENT_TAB,
+                             ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
+                             /*is_renderer_initiated=*/false),
       /*navigation_handle_callback=*/{});
 
   base::RunLoop().RunUntilIdle();
@@ -351,7 +425,7 @@
   // Navigate to a page where there is a shouldProceed query param that denotes
   // failure.
   GetPopupWebContents()->OpenURL(
-      content::OpenURLParams(GURL("https://site.example/?shouldProceed=false"),
+      content::OpenURLParams(GURL("https://site.example/?failure=true"),
                              content::Referrer(),
                              WindowOpenDisposition::CURRENT_TAB,
                              ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
@@ -373,13 +447,13 @@
   ShowUi("Vcn3ds_ConsentAlreadyGiven");
   EXPECT_TRUE(VerifyUi());
 
-  // Navigate to a page where there are shouldProceed and token query params.
+  // Navigate to a page where there is a token query param.
   GetPopupWebContents()->OpenURL(
-      content::OpenURLParams(
-          GURL("https://site.example/?shouldProceed=true&token=sometesttoken"),
-          content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
-          ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
-          /*is_renderer_initiated=*/false),
+      content::OpenURLParams(GURL("https://site.example/?token=sometesttoken"),
+                             content::Referrer(),
+                             WindowOpenDisposition::CURRENT_TAB,
+                             ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
+                             /*is_renderer_initiated=*/false),
       /*navigation_handle_callback=*/{});
 
   ClosePopup();
@@ -396,8 +470,10 @@
                                ->GetPaymentsNetworkInterface())
                            ->unmask_request();
   ASSERT_TRUE(unmask_request.has_value());
-  EXPECT_EQ(unmask_request->card, card_);
-  EXPECT_EQ(unmask_request->redirect_completion_proof.value(), "sometesttoken");
+  EXPECT_EQ(unmask_request->card,
+            test_api(window_manager()).GetVcn3dsContext()->card);
+  EXPECT_EQ(unmask_request->redirect_completion_result.value(),
+            "sometesttoken");
   EXPECT_EQ(unmask_request->last_committed_primary_main_frame_origin,
             client()->GetLastCommittedPrimaryMainFrameOrigin().GetURL());
 
@@ -422,13 +498,13 @@
   ShowUi("Vcn3ds_ConsentAlreadyGiven");
   EXPECT_TRUE(VerifyUi());
 
-  // Navigate to a page where there are shouldProceed and token query params.
+  // Navigate to a page where there is a token query param.
   GetPopupWebContents()->OpenURL(
-      content::OpenURLParams(
-          GURL("https://site.example/?shouldProceed=true&token=sometesttoken"),
-          content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
-          ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
-          /*is_renderer_initiated=*/false),
+      content::OpenURLParams(GURL("https://site.example/?token=sometesttoken"),
+                             content::Referrer(),
+                             WindowOpenDisposition::CURRENT_TAB,
+                             ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
+                             /*is_renderer_initiated=*/false),
       /*navigation_handle_callback=*/{});
 
   ClosePopup();
@@ -456,10 +532,10 @@
   ShowUi("Vcn3ds_ConsentAlreadyGiven");
   EXPECT_TRUE(VerifyUi());
 
-  // Navigate to a page where there is an shouldProceed query param that denotes
+  // Navigate to a page where there is a failure query param that denotes
   // the authentication failed.
   GetPopupWebContents()->OpenURL(
-      content::OpenURLParams(GURL("https://site.example/?shouldProceed=false"),
+      content::OpenURLParams(GURL("https://site.example/?failure=true"),
                              content::Referrer(),
                              WindowOpenDisposition::CURRENT_TAB,
                              ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
@@ -493,10 +569,10 @@
   ShowUi("Vcn3ds_ConsentAlreadyGiven");
   EXPECT_TRUE(VerifyUi());
 
-  // Navigate to a page where there is an shouldProceed query param that denotes
+  // Navigate to a page where there is a failure query param that denotes
   // the authentication failed.
   GetPopupWebContents()->OpenURL(
-      content::OpenURLParams(GURL("https://site.example/?shouldProceed=false"),
+      content::OpenURLParams(GURL("https://site.example/?failure=true"),
                              content::Referrer(),
                              WindowOpenDisposition::CURRENT_TAB,
                              ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
@@ -600,13 +676,13 @@
   ShowUi("Vcn3ds_ConsentAlreadyGiven");
   EXPECT_TRUE(VerifyUi());
 
-  // Navigate to a page where there are shouldProceed and token query params.
+  // Navigate to a page where there is a token query param.
   GetPopupWebContents()->OpenURL(
-      content::OpenURLParams(
-          GURL("https://site.example/?shouldProceed=true&token=sometesttoken"),
-          content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
-          ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
-          /*is_renderer_initiated=*/false),
+      content::OpenURLParams(GURL("https://site.example/?token=sometesttoken"),
+                             content::Referrer(),
+                             WindowOpenDisposition::CURRENT_TAB,
+                             ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
+                             /*is_renderer_initiated=*/false),
       /*navigation_handle_callback=*/{});
 
   base::RunLoop().RunUntilIdle();
@@ -635,13 +711,13 @@
   ShowUi("Vcn3ds_ConsentAlreadyGiven");
   EXPECT_TRUE(VerifyUi());
 
-  // Navigate to a page where there are shouldProceed and token query params.
+  // Navigate to a page where there is a token query param.
   GetPopupWebContents()->OpenURL(
-      content::OpenURLParams(
-          GURL("https://site.example/?shouldProceed=true&token=sometesttoken"),
-          content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
-          ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
-          /*is_renderer_initiated=*/false),
+      content::OpenURLParams(GURL("https://site.example/?token=sometesttoken"),
+                             content::Referrer(),
+                             WindowOpenDisposition::CURRENT_TAB,
+                             ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
+                             /*is_renderer_initiated=*/false),
       /*navigation_handle_callback=*/{});
 
   base::RunLoop().RunUntilIdle();
@@ -730,7 +806,11 @@
       PaymentsWindowManager::Vcn3dsContext context;
       context.card = test::GetVirtualCard();
       context.context_token = kTestContextToken;
-      context.challenge_option.url_to_open = GURL(kVcn3dsTestUrl);
+      Vcn3dsChallengeOptionMetadata metadata;
+      metadata.url_to_open = GURL(kVcn3dsTestUrl);
+      metadata.success_query_param_name = "token";
+      metadata.failure_query_param_name = "failure";
+      context.challenge_option.vcn_3ds_metadata = std::move(metadata);
       context.completion_callback = authentication_complete_callback_.Get();
       context.user_consent_already_given = false;
       ON_CALL(authentication_complete_callback_, Run)
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_open_metrics.cc b/chrome/browser/ui/webui/ash/cloud_upload/cloud_open_metrics.cc
index 330849b..4ae07c3 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/cloud_open_metrics.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_open_metrics.cc
@@ -70,6 +70,7 @@
     case OfficeTaskResult::kCannotShowSetupDialog:
     case OfficeTaskResult::kNoFilesToOpen:
     case OfficeTaskResult::kOkAtFallback:
+    case OfficeTaskResult::kFileAlreadyBeingOpened:
       return true;
     case OfficeTaskResult::kOpened:
     case OfficeTaskResult::kMoved:
@@ -113,6 +114,7 @@
     case OfficeTaskResult::kCannotShowSetupDialog:
     case OfficeTaskResult::kCannotShowMoveConfirmation:
     case OfficeTaskResult::kNoFilesToOpen:
+    case OfficeTaskResult::kFileAlreadyBeingOpened:
       return false;
   }
 }
@@ -143,6 +145,7 @@
     case OfficeTaskResult::kFallbackQuickOfficeAfterOpen:
     case OfficeTaskResult::kCancelledAtFallbackAfterOpen:
     case OfficeTaskResult::kCannotGetFallbackChoiceAfterOpen:
+    case OfficeTaskResult::kFileAlreadyBeingOpened:
       return false;
   }
 }
@@ -598,10 +601,8 @@
     } else {
       // TransferRequired was kCopy or kMove.
       if (task_result.logged() &&
-          (DidEndAtMoveConfirmation(task_result.value) ||
-           task_result.value == OfficeTaskResult::kFileAlreadyBeingUploaded)) {
-        // The cloud upload flow was exited at the Move Confirmation Dialog or
-        // the upload was abandoned.
+          (DidEndAtMoveConfirmation(task_result.value))) {
+        // The cloud upload flow was exited at the Move Confirmation Dialog.
         ExpectNotLogged(upload_result);
       } else {
         // The upload should have succeeded or failed.
@@ -697,6 +698,7 @@
           case OfficeTaskResult::kFallbackQuickOfficeAfterOpen:
           case OfficeTaskResult::kCancelledAtFallbackAfterOpen:
           case OfficeTaskResult::kCannotGetFallbackChoiceAfterOpen:
+          case OfficeTaskResult::kFileAlreadyBeingOpened:
             SetWrongValueLogged(task_result);
             break;
         }
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_open_metrics_unittest.cc b/chrome/browser/ui/webui/ash/cloud_upload/cloud_open_metrics_unittest.cc
index 009d617..01f180a 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/cloud_open_metrics_unittest.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_open_metrics_unittest.cc
@@ -543,38 +543,32 @@
   ASSERT_EQ(1, CloudOpenMetricsTest::number_of_dump_calls());
 }
 
-// Tests that the UploadResult companion metric is set correctly when
-// TransferRequired is logged as kCopy and TaskResult is
-// kFileAlreadyBeingUploaded and it is logged consistently.
-TEST_F(
-    CloudOpenMetricsTest,
-    MetricsConsistentWhenTransferRequiredIsCopyAndTaskResultIsFileAlreadyBeingUploaded) {
+// Tests that the TransferRequired companion metric is set correctly when
+// TaskResult is kFileAlreadyBeingOpened and it is logged consistently.
+TEST_F(CloudOpenMetricsTest,
+       MetricsConsistentWhenTaskResultIsFileAlreadyBeingOpened) {
   {
     CloudOpenMetrics cloud_open_metrics(CloudProvider::kOneDrive,
                                         /*file_count=*/1);
-    cloud_open_metrics.LogTransferRequired(OfficeFilesTransferRequired::kCopy);
-    cloud_open_metrics.LogTaskResult(
-        OfficeTaskResult::kFileAlreadyBeingUploaded);
+    cloud_open_metrics.LogTaskResult(OfficeTaskResult::kFileAlreadyBeingOpened);
   }
-  histogram_.ExpectUniqueSample(kOneDriveUploadResultMetricStateMetricName,
+  histogram_.ExpectUniqueSample(kOneDriveTransferRequiredMetricStateMetric,
                                 MetricState::kCorrectlyNotLogged, 1);
 }
 
-// Tests that the UploadResult companion metric is set correctly when
-// TransferRequired is logged as kCopy and TaskResult is
-// kFileAlreadyBeingUploaded and it is logged inconsistently.
+// Tests that the TransferRequired companion metric is set incorrectly when
+// TaskResult is kFileAlreadyBeingOpened and it is logged consistently.
 TEST_F(
     CloudOpenMetricsTest,
-    MetricsInconsistentWhenTransferRequiredIsCopyAndTaskResultIsFileAlreadyBeingUploaded) {
+    MetricsInconsistentWhenTaskResultIsFileAlreadyBeingOpenedAndTransferRequiredLogged) {
   {
     CloudOpenMetrics cloud_open_metrics(CloudProvider::kOneDrive,
                                         /*file_count=*/1);
+    // No TransferRequired should be logged.
     cloud_open_metrics.LogTransferRequired(OfficeFilesTransferRequired::kCopy);
-    cloud_open_metrics.LogTaskResult(
-        OfficeTaskResult::kFileAlreadyBeingUploaded);
-    cloud_open_metrics.LogUploadResult(OfficeFilesUploadResult::kSuccess);
+    cloud_open_metrics.LogTaskResult(OfficeTaskResult::kFileAlreadyBeingOpened);
   }
-  histogram_.ExpectUniqueSample(kOneDriveUploadResultMetricStateMetricName,
+  histogram_.ExpectUniqueSample(kOneDriveTransferRequiredMetricStateMetric,
                                 MetricState::kIncorrectlyLogged, 1);
 }
 
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc
index fd2af91..498eeb4 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ash/arc/fileapi/arc_documents_provider_util.h"
+#include "chrome/browser/ash/extensions/file_manager/event_router_factory.h"
 #include "chrome/browser/ash/file_manager/file_tasks.h"
 #include "chrome/browser/ash/file_manager/io_task.h"
 #include "chrome/browser/ash/file_manager/office_file_tasks.h"
@@ -432,6 +433,14 @@
 }
 
 CloudOpenTask::~CloudOpenTask() {
+  auto* event_router =
+      file_manager::EventRouterFactory::GetForProfile(profile_);
+  DCHECK(!file_urls_.empty());
+  if (event_router) {
+    event_router->RemoveCloudOpenTask(file_urls_.front());
+  } else {
+    LOG(ERROR) << "Cannot get EventRouter";
+  }
   BrowserList::RemoveObserver(this);
 }
 
@@ -446,6 +455,20 @@
     return false;
   }
 
+  auto* event_router =
+      file_manager::EventRouterFactory::GetForProfile(profile_);
+  // TODO(b/242685536) add support for multiple files.
+  if (event_router) {
+    if (!event_router->AddCloudOpenTask(file_urls_.front())) {
+      LOG(ERROR) << "File already being opened";
+      cloud_open_metrics_->LogTaskResult(
+          OfficeTaskResult::kFileAlreadyBeingOpened);
+      return false;
+    }
+  } else {
+    LOG(ERROR) << "Cannot get EventRouter";
+  }
+
   // Run the setup flow if we don't have explicit default file handlers set for
   // these files in preferences. This indicates we haven't run setup, because
   // setup sets default handlers at the end. If the user has a default set for
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
index 0fb99575..28b3a2e7 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
@@ -174,7 +174,8 @@
   kFallbackQuickOfficeAfterOpen = 18,
   kCancelledAtFallbackAfterOpen = 19,
   kCannotGetFallbackChoiceAfterOpen = 20,
-  kMaxValue = kCannotGetFallbackChoiceAfterOpen,
+  kFileAlreadyBeingOpened = 21,
+  kMaxValue = kFileAlreadyBeingOpened,
 };
 
 // The result of the "Upload to cloud" workflow for Office files.
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
index 647cb7b..355f7fb 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
@@ -122,32 +122,6 @@
           weak_ptr_factory_.GetWeakPtr(), destination_folder_url));
 }
 
-bool OneDriveUploadHandler::FileAlreadyBeingUploaded() {
-  for (std::reference_wrapper<const file_manager::io_task::ProgressStatus>
-           status : io_task_controller_->TaskStatuses()) {
-    // Check upload (copy/move) tasks.
-    if (status.get().type != file_manager::io_task::OperationType::kCopy &&
-        status.get().type != file_manager::io_task::OperationType::kMove) {
-      continue;
-    }
-
-    // Check upload to ODFS tasks.
-    if (!UrlIsOnODFS(status.get().GetDestinationFolder())) {
-      continue;
-    }
-
-    for (const auto& entry_status : status.get().sources) {
-      if (entry_status.url == source_url_) {
-        // The same source url is being uploaded to ODFS.
-        return true;
-      }
-    }
-  }
-
-  // No duplicate task.
-  return false;
-}
-
 void OneDriveUploadHandler::CheckReauthenticationAndStartIOTask(
     const FileSystemURL& destination_folder_url,
     base::expected<ODFSMetadata, base::File::Error> metadata_or_error) {
@@ -173,12 +147,6 @@
     return;
   }
 
-  if (FileAlreadyBeingUploaded()) {
-    LOG(WARNING) << "File already being uploaded";
-    OnAbandonedUpload();
-    return;
-  }
-
   operation_type_ = GetUploadType(profile_, source_url_) == UploadType::kCopy
                         ? file_manager::io_task::OperationType::kCopy
                         : file_manager::io_task::OperationType::kMove;
@@ -238,14 +206,6 @@
                              0);
 }
 
-void OneDriveUploadHandler::OnAbandonedUpload() {
-  if (notification_manager_) {
-    notification_manager_->CloseNotification();
-  }
-    std::move(callback_).Run(OfficeTaskResult::kFileAlreadyBeingUploaded,
-                             std::nullopt, 0);
-}
-
 void OneDriveUploadHandler::OnIOTaskStatus(
     const file_manager::io_task::ProgressStatus& status) {
   if (status.task_id != observed_task_id_) {
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
index 7ff05fe2..338b295 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
@@ -51,9 +51,6 @@
   OneDriveUploadHandler& operator=(const OneDriveUploadHandler&) = delete;
 
  private:
-  // Checks if there already exists an upload task for this file.
-  bool FileAlreadyBeingUploaded();
-
   void GetODFSMetadataAndStartIOTask();
 
   // If reauth is required, request a new mount without a notification. If that
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler_browsertest.cc b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler_browsertest.cc
index ab6cebc5..0443a64c 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler_browsertest.cc
@@ -697,51 +697,6 @@
                                 OfficeFilesUploadResult::kCloudAccessDenied, 1);
 }
 
-// Tests that a duplicate upload is abandoned.
-IN_PROC_BROWSER_TEST_F(OneDriveUploadHandlerTest, NoDuplicateUploads) {
-  SetUpObservers();
-  SetUpMyFiles();
-  SetUpODFS();
-  const std::string test_file_name = "text.docx";
-  FileSystemURL source_file_url = CopyTestFile(test_file_name, my_files_dir_);
-
-  // Start the second upload after the first one starts.
-  provided_file_system_->SetCreateFileCallback(
-      base::BindLambdaForTesting([&]() {
-        auto one_drive_upload_handler2 =
-            std::make_unique<OneDriveUploadHandler>(
-                profile(), source_file_url,
-                base::BindOnce(
-                    &OneDriveUploadHandlerTest::OnUploadFailedOrAbandoned,
-                    base::Unretained(this), /*expected_task_result=*/
-                    OfficeTaskResult::kFileAlreadyBeingUploaded),
-                cloud_open_metrics_ref_);
-        one_drive_upload_handler2->Run();
-      }));
-
-  // Start the first upload.
-  auto one_drive_upload_handler1 = std::make_unique<OneDriveUploadHandler>(
-      profile(), source_file_url,
-      base::BindOnce(&OneDriveUploadHandlerTest::OnUploadSuccessful,
-                     base::Unretained(this),
-                     /*expected_task_result=*/OfficeTaskResult::kMoved),
-      cloud_open_metrics_ref_);
-  one_drive_upload_handler1->Run();
-
-  SetUpRunLoopAndWait(/*conditions_to_end_wait=*/2);
-
-  // Check that the source file has been moved to OneDrive.
-  {
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    EXPECT_FALSE(base::PathExists(my_files_dir_.AppendASCII(test_file_name)));
-    CheckPathExistsOnODFS(base::FilePath("/").AppendASCII(test_file_name));
-  }
-
-  // There should only be one UploadResult from the first upload.
-  histogram_.ExpectUniqueSample(kOneDriveUploadResultMetricName,
-                                OfficeFilesUploadResult::kSuccess, 1);
-}
-
 // Tests that when there is an upload occurring followed by a second, unrelated
 // upload, both are successful.
 IN_PROC_BROWSER_TEST_F(OneDriveUploadHandlerTest, UnrelatedUploads) {
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index da13b263..7b08ca2 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1716097839-e9677c5882f1efdb15d42fd43d52900085e4611c-624deb932496db45e7f2ac361671685c47da4609.profdata
+chrome-android32-main-1716162733-52d1510967f6c4202e18c768e9d0f1f14b73b0ed-81200e6683ebd81d70fff24718fe5f791a81df63.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 58974280..af0b7dee 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1716097839-3ba5c74e0b79858009aacd82631126aa75d2adb0-624deb932496db45e7f2ac361671685c47da4609.profdata
+chrome-android64-main-1716119410-7c8d07fbb87dcb154f199e160e16aa42fdd88ced-5bb8c37953021bed0b611699c33ce316e4ff2637.profdata
diff --git a/chrome/build/lacros64.pgo.txt b/chrome/build/lacros64.pgo.txt
index 5083e45..616c30c 100644
--- a/chrome/build/lacros64.pgo.txt
+++ b/chrome/build/lacros64.pgo.txt
@@ -1 +1 @@
-chrome-chromeos-amd64-generic-main-1716076133-c4b5b724ea4b949ea3ce1d373eef082b559ce2cd-a3d6b46084a1d13174e1a030c64fb44a1812b5ad.profdata
+chrome-chromeos-amd64-generic-main-1716163684-f986bf14b099725cfb5b10b1860ebd7c342c7a37-0a7b0680b2d8f69e12bc94d71d70a9307188df02.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 4b50f71..3f17324 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1716097839-a099458716b3b30e1958cc28201b052c9cc6f4f7-624deb932496db45e7f2ac361671685c47da4609.profdata
+chrome-linux-main-1716162733-132edb14d4da5dcd7db4466036c5a28d8d8af6c1-81200e6683ebd81d70fff24718fe5f791a81df63.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 9f4f5e6..8a313f3 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1716104860-34e0ab4103dd5108856109d8db7c3f9ede9e9beb-d1d2352c0fb4952e569c4b33d492c634572b1474.profdata
+chrome-mac-arm-main-1716177571-cfb4ea842a4a16d5c5481ceafacabe0cd8b4cfdd-980f3f6bf0af218e4035bc2e5b6a5c803e35ce9d.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 0736d78c..286b5d7 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1716097839-0212c4becb704f9f49178b66807d6617f5a6b3ca-624deb932496db45e7f2ac361671685c47da4609.profdata
+chrome-win-arm64-main-1716162733-4a9a5913fa0056bfcd7a207cc861c8b73bccc9ed-81200e6683ebd81d70fff24718fe5f791a81df63.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 196b057..c53cb504 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1716097839-d958aaa233411095dd361226f701bd0446053b68-624deb932496db45e7f2ac361671685c47da4609.profdata
+chrome-win32-main-1716173901-504ae28eae954798de3c7bd15cc78ccb5da9314f-4a34b8a3d02306c95d58c6d7c9ede6bb35ea4b8d.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index bcac61de..c3265fc 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1716097839-9338beaa53ea78dbfa59095e69195085cf4f2e03-624deb932496db45e7f2ac361671685c47da4609.profdata
+chrome-win64-main-1716162733-d25be3c097cbb802a774b202ff976b49244e4427-81200e6683ebd81d70fff24718fe5f791a81df63.profdata
diff --git a/chrome/renderer/autofill/form_autocomplete_browsertest.cc b/chrome/renderer/autofill/form_autocomplete_browsertest.cc
index 372e36fa..7a9ee1b 100644
--- a/chrome/renderer/autofill/form_autocomplete_browsertest.cc
+++ b/chrome/renderer/autofill/form_autocomplete_browsertest.cc
@@ -85,6 +85,10 @@
     submission_source_ = source;
   }
 
+  void CaretMovedInFormField(const FormData& form,
+                             const FormFieldData& field,
+                             const gfx::Rect& caret_bounds) override {}
+
   void TextFieldDidChange(const FormData& form,
                           const FormFieldData& field,
                           base::TimeTicks timestamp) override {}
diff --git a/chrome/renderer/autofill/password_generation_agent_browsertest.cc b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
index b80ea6e..9db6475f 100644
--- a/chrome/renderer/autofill/password_generation_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
@@ -93,6 +93,10 @@
                      bool known_success,
                      mojom::SubmissionSource source) override {}
 
+  void CaretMovedInFormField(const FormData& form,
+                             const FormFieldData& field,
+                             const gfx::Rect& caret_bounds) override {}
+
   void TextFieldDidChange(const FormData& form,
                           const FormFieldData& field,
                           base::TimeTicks timestamp) override {}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b37715c..d9ece3d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3050,6 +3050,14 @@
         "../browser/screen_ai/optical_character_recognizer_browsertest.cc",
         "../browser/screen_ai/screen_ai_service_router_browsertest.cc",
       ]
+
+      if (enable_screen_ai_browsertests && !use_fake_screen_ai) {
+        # TODO(crbug.com/41489544): Update when fake ScreenAI library supports
+        # `ExtractMainContent` requests.
+        sources +=
+            [ "../browser/screen_ai/main_content_extraction_browsertest.cc" ]
+      }
+
       deps += [
         "//chrome/browser/screen_ai:screen_ai_install_state",
         "//chrome/browser/screen_ai:screen_ai_service_router_factory",
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 20d415e..bf89378f 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-15890.0.0
\ No newline at end of file
+15894.0.0
\ No newline at end of file
diff --git a/chromeos/ash/components/dbus/shill/fake_shill_manager_client.cc b/chromeos/ash/components/dbus/shill/fake_shill_manager_client.cc
index ecf6cfc5..c3521b11 100644
--- a/chromeos/ash/components/dbus/shill/fake_shill_manager_client.cc
+++ b/chromeos/ash/components/dbus/shill/fake_shill_manager_client.cc
@@ -858,6 +858,10 @@
   SetProperty(key, value, base::DoNothing(), base::BindOnce(&LogErrorCallback));
 }
 
+base::Value::Dict FakeShillManagerClient::GetStubProperties() {
+  return stub_properties_.Clone();
+}
+
 void FakeShillManagerClient::AddManagerService(const std::string& service_path,
                                                bool notify_observers) {
   VLOG(2) << "AddManagerService: " << service_path;
diff --git a/chromeos/ash/components/dbus/shill/fake_shill_manager_client.h b/chromeos/ash/components/dbus/shill/fake_shill_manager_client.h
index f24dd6a..0df21c7d 100644
--- a/chromeos/ash/components/dbus/shill/fake_shill_manager_client.h
+++ b/chromeos/ash/components/dbus/shill/fake_shill_manager_client.h
@@ -127,6 +127,7 @@
   void ClearProperties() override;
   void SetManagerProperty(const std::string& key,
                           const base::Value& value) override;
+  base::Value::Dict GetStubProperties() override;
   void AddManagerService(const std::string& service_path,
                          bool notify_observers) override;
   void RemoveManagerService(const std::string& service_path) override;
diff --git a/chromeos/ash/components/dbus/shill/shill_manager_client.h b/chromeos/ash/components/dbus/shill/shill_manager_client.h
index bbc7006..690d91d 100644
--- a/chromeos/ash/components/dbus/shill/shill_manager_client.h
+++ b/chromeos/ash/components/dbus/shill/shill_manager_client.h
@@ -163,6 +163,9 @@
     virtual void SetManagerProperty(const std::string& key,
                                     const base::Value& value) = 0;
 
+    // Get stub manager properties.
+    virtual base::Value::Dict GetStubProperties() = 0;
+
     // Modify services in the Manager's list.
     virtual void AddManagerService(const std::string& service_path,
                                    bool notify_observers) = 0;
diff --git a/chromeos/ash/components/network/managed_network_configuration_handler_impl.cc b/chromeos/ash/components/network/managed_network_configuration_handler_impl.cc
index 28b735e..eb04f042 100644
--- a/chromeos/ash/components/network/managed_network_configuration_handler_impl.cc
+++ b/chromeos/ash/components/network/managed_network_configuration_handler_impl.cc
@@ -571,10 +571,35 @@
       userhash, policies->ApplyOncNetworkConfigurationList(network_configs_onc),
       /*can_affect_other_networks=*/true, /*options=*/{});
 
+  ApplyDisconnectWiFiOnEthernetPolicy();
+
   for (auto& observer : observers_)
     observer.PoliciesChanged(userhash);
 }
 
+void ManagedNetworkConfigurationHandlerImpl::
+    ApplyDisconnectWiFiOnEthernetPolicy() {
+  const std::string* disconnect_wifi_policy = FindGlobalPolicyString(
+      ::onc::global_network_config::kDisconnectWiFiOnEthernet);
+  base::Value shill_property_value =
+      base::Value(shill::kDisconnectWiFiOnEthernetOff);
+  if (disconnect_wifi_policy) {
+    if (*disconnect_wifi_policy ==
+        ::onc::global_network_config::kDisconnectWiFiOnEthernetWhenConnected) {
+      shill_property_value =
+          base::Value(shill::kDisconnectWiFiOnEthernetConnected);
+    }
+    if (*disconnect_wifi_policy ==
+        ::onc::global_network_config::kDisconnectWiFiOnEthernetWhenOnline) {
+      shill_property_value =
+          base::Value(shill::kDisconnectWiFiOnEthernetOnline);
+    }
+  }
+  network_configuration_handler_->SetManagerProperty(
+      shill::kDisconnectWiFiOnEthernetProperty, shill_property_value);
+  return;
+}
+
 bool ManagedNetworkConfigurationHandlerImpl::IsAnyPolicyApplicationRunning()
     const {
   for (const auto& [_, policy_application_info] :
diff --git a/chromeos/ash/components/network/managed_network_configuration_handler_impl.h b/chromeos/ash/components/network/managed_network_configuration_handler_impl.h
index 604c7c6..f86a1a2 100644
--- a/chromeos/ash/components/network/managed_network_configuration_handler_impl.h
+++ b/chromeos/ash/components/network/managed_network_configuration_handler_impl.h
@@ -335,6 +335,10 @@
                             bool can_affect_other_networks,
                             PolicyApplicator::Options options);
 
+  // Called in SetPolicy, sets shill DisconnectWiFiOnEthernet Manager property
+  // base on value of DisconnectWiFiOnEthernet GlobalNetworkConfiguration.
+  void ApplyDisconnectWiFiOnEthernetPolicy();
+
   void SchedulePolicyApplication(const std::string& userhash);
   void StartPolicyApplication(const std::string& userhash);
 
diff --git a/chromeos/ash/components/network/managed_network_configuration_handler_unittest.cc b/chromeos/ash/components/network/managed_network_configuration_handler_unittest.cc
index e389aa2a..cef5928 100644
--- a/chromeos/ash/components/network/managed_network_configuration_handler_unittest.cc
+++ b/chromeos/ash/components/network/managed_network_configuration_handler_unittest.cc
@@ -2032,6 +2032,80 @@
   EXPECT_TRUE(managed_handler()->GetBlockedHexSSIDs().empty());
 }
 
+TEST_F(ManagedNetworkConfigurationHandlerTest, DisconnectWiFiOnEthernet) {
+  policy_util::SetEphemeralNetworkPoliciesEnabled();
+
+  const char* const onc_policy_connected = R"(
+      {
+        "GlobalNetworkConfiguration": {
+          "DisconnectWiFiOnEthernet": "WhenConnected"
+        },
+        "Type": "UnencryptedConfiguration"
+      })";
+  EXPECT_TRUE(SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
+                        base::test::ParseJsonDict(onc_policy_connected)));
+  FastForwardProfileRefreshDelay();
+  base::RunLoop().RunUntilIdle();
+  auto properties =
+      ShillManagerClient::Get()->GetTestInterface()->GetStubProperties();
+  EXPECT_NE(properties.FindString(shill::kDisconnectWiFiOnEthernetProperty),
+            nullptr);
+  EXPECT_EQ(*properties.FindString(shill::kDisconnectWiFiOnEthernetProperty),
+            std::string(shill::kDisconnectWiFiOnEthernetConnected));
+
+  const char* const onc_policy_invalid = R"(
+      {
+        "GlobalNetworkConfiguration": {
+          "DisconnectWiFiOnEthernet": "InvalidValue"
+        },
+        "Type": "UnencryptedConfiguration"
+      })";
+  EXPECT_TRUE(SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
+                        base::test::ParseJsonDict(onc_policy_invalid)));
+  FastForwardProfileRefreshDelay();
+  base::RunLoop().RunUntilIdle();
+  properties =
+      ShillManagerClient::Get()->GetTestInterface()->GetStubProperties();
+  EXPECT_NE(properties.FindString(shill::kDisconnectWiFiOnEthernetProperty),
+            nullptr);
+  EXPECT_EQ(*properties.FindString(shill::kDisconnectWiFiOnEthernetProperty),
+            std::string(shill::kDisconnectWiFiOnEthernetOff));
+
+  const char* const onc_policy_online = R"(
+      {
+        "GlobalNetworkConfiguration": {
+          "DisconnectWiFiOnEthernet": "WhenOnline"
+        },
+        "Type": "UnencryptedConfiguration"
+      })";
+  EXPECT_TRUE(SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
+                        base::test::ParseJsonDict(onc_policy_online)));
+  FastForwardProfileRefreshDelay();
+  base::RunLoop().RunUntilIdle();
+  properties =
+      ShillManagerClient::Get()->GetTestInterface()->GetStubProperties();
+  EXPECT_NE(properties.FindString(shill::kDisconnectWiFiOnEthernetProperty),
+            nullptr);
+  EXPECT_EQ(*properties.FindString(shill::kDisconnectWiFiOnEthernetProperty),
+            std::string(shill::kDisconnectWiFiOnEthernetOnline));
+
+  const char* const onc_policy_off = R"(
+      {
+        "GlobalNetworkConfiguration": {},
+        "Type": "UnencryptedConfiguration"
+      })";
+  EXPECT_TRUE(SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
+                        base::test::ParseJsonDict(onc_policy_off)));
+  FastForwardProfileRefreshDelay();
+  base::RunLoop().RunUntilIdle();
+  properties =
+      ShillManagerClient::Get()->GetTestInterface()->GetStubProperties();
+  EXPECT_NE(properties.FindString(shill::kDisconnectWiFiOnEthernetProperty),
+            nullptr);
+  EXPECT_EQ(*properties.FindString(shill::kDisconnectWiFiOnEthernetProperty),
+            std::string(shill::kDisconnectWiFiOnEthernetOff));
+}
+
 TEST_F(ManagedNetworkConfigurationHandlerTest,
        RecommendedValuesAreEphemeralAccessor) {
   policy_util::SetEphemeralNetworkPoliciesEnabled();
diff --git a/chromeos/components/onc/onc_signature.cc b/chromeos/components/onc/onc_signature.cc
index 4b1597e..de4bb67 100644
--- a/chromeos/components/onc/onc_signature.cc
+++ b/chromeos/components/onc/onc_signature.cc
@@ -429,6 +429,8 @@
      &kStringListSignature},
     {::onc::global_network_config::kPSIMAdminAssignedAPNs,
      &kCellularApnListSignature},
+    {::onc::global_network_config::kDisconnectWiFiOnEthernet,
+     &kStringSignature},
     {nullptr}};
 
 const OncFieldSignature certificate_fields[] = {
diff --git a/chromeos/components/onc/onc_validator.cc b/chromeos/components/onc/onc_validator.cc
index 3991ed3..f68e856a 100644
--- a/chromeos/components/onc/onc_validator.cc
+++ b/chromeos/components/onc/onc_validator.cc
@@ -1383,7 +1383,8 @@
       ::onc::global_network_config::kBlockedHexSSIDs,
       ::onc::global_network_config::kRecommendedValuesAreEphemeral,
       ::onc::global_network_config::
-          kUserCreatedNetworkConfigurationsAreEphemeral};
+          kUserCreatedNetworkConfigurationsAreEphemeral,
+      ::onc::global_network_config::kDisconnectWiFiOnEthernet};
   for (std::string_view key : kDevicePolicyOnlyKeys) {
     if (!IsInDevicePolicy(result, key)) {
       return false;
diff --git a/clank b/clank
index deb0fef..6bd5196 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit deb0fefc7e682a14b37e45913a6c8ac81ab8d029
+Subproject commit 6bd51962cb20205c2a1e8fa0ed704360034d6cf3
diff --git a/components/android_autofill/browser/android_autofill_manager.h b/components/android_autofill/browser/android_autofill_manager.h
index bb7d3b8..7286e461 100644
--- a/components/android_autofill/browser/android_autofill_manager.h
+++ b/components/android_autofill/browser/android_autofill_manager.h
@@ -77,6 +77,10 @@
                            bool known_success,
                            mojom::SubmissionSource source) override;
 
+  void OnCaretMovedInFormFieldImpl(const FormData& form,
+                                   const FormFieldData& field,
+                                   const gfx::Rect& caret_bounds) override {}
+
   void OnTextFieldDidChangeImpl(const FormData& form,
                                 const FormFieldData& field,
                                 const base::TimeTicks timestamp) override;
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index e2ed7f6e..bae948a 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -439,6 +439,26 @@
       });
 }
 
+void ContentAutofillDriver::CaretMovedInFormField(
+    const FormData& raw_form,
+    const FormFieldData& raw_field,
+    const gfx::Rect& caret_bounds) {
+  if (!bad_message::CheckFrameNotPrerendering(render_frame_host())) {
+    return;
+  }
+  FormData form = raw_form;
+  FormFieldData field = raw_field;
+  SetFrameAndFormMetaData(form, field);
+  router().CaretMovedInFormField(
+      *this, std::move(form), field,
+      TransformBoundingBoxToViewportCoordinates(caret_bounds),
+      [](autofill::AutofillDriver& target, const FormData& form,
+         const FormFieldData& field, const gfx::Rect& caret_bounds) {
+        target.GetAutofillManager().OnCaretMovedInFormField(
+            WithNewVersion(form), field, caret_bounds);
+      });
+}
+
 void ContentAutofillDriver::TextFieldDidChange(const FormData& raw_form,
                                                const FormFieldData& raw_field,
                                                base::TimeTicks timestamp) {
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index 3603c6b..7bb676b 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -264,6 +264,9 @@
   void SelectControlDidChange(const FormData& form,
                               const FormFieldData& field) override;
   void SelectOrSelectListFieldOptionsDidChange(const FormData& form) override;
+  void CaretMovedInFormField(const FormData& form,
+                             const FormFieldData& field,
+                             const gfx::Rect& caret_bounds) override;
   void TextFieldDidChange(const FormData& form,
                           const FormFieldData& field,
                           base::TimeTicks timestamp) override;
diff --git a/components/autofill/content/common/mojom/autofill_driver.mojom b/components/autofill/content/common/mojom/autofill_driver.mojom
index b83b2849f..1873e72 100644
--- a/components/autofill/content/common/mojom/autofill_driver.mojom
+++ b/components/autofill/content/common/mojom/autofill_driver.mojom
@@ -27,6 +27,18 @@
                 bool known_success,
                 SubmissionSource source);
 
+  // Notification that a form field's caret moved or selection has changed.
+  //
+  // `caret_bounds` is the bounds of the caret if the selection is empty and
+  // otherwise the bounds of the focus
+  // (https://w3c.github.io/selection-api/#dfn-focus). That is, its width is
+  // typically zero or near-zero, and its height is about one line. It is not
+  // validated in any way; in particular, the coordinates may be negative or
+  // otherwise outside of the bounds of the viewport.
+  CaretMovedInFormField(FormData form,
+                        FormFieldData field,
+                        gfx.mojom.Rect caret_bounds);
+
   // Notification that a form field's value has changed by a user edit.
   //
   // Similarly to JavaScript's "input" event, and unlike JavaScript's "change"
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index a70ebad..a31d4863 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -176,6 +176,12 @@
     DeferMsg(&mojom::AutofillDriver::FormSubmitted, form, known_success,
              source);
   }
+  void CaretMovedInFormField(const FormData& form,
+                             const FormFieldData& field,
+                             const gfx::Rect& caret_bounds) override {
+    DeferMsg(&mojom::AutofillDriver::CaretMovedInFormField, form, field,
+             caret_bounds);
+  }
   void TextFieldDidChange(const FormData& form,
                           const FormFieldData& field,
                           base::TimeTicks timestamp) override {
@@ -495,6 +501,8 @@
 
 void AutofillAgent::FocusedElementChanged(
     const WebElement& new_focused_element) {
+  ObserveCaret(new_focused_element);
+
   if (!base::FeatureList::IsEnabled(features::kAutofillNewFocusEvents)) {
     FocusedElementChangedDeprecated(new_focused_element);
     return;
@@ -558,6 +566,75 @@
   }
 }
 
+void AutofillAgent::ObserveCaret(WebElement element) {
+  if (auto control = element.DynamicTo<WebFormControlElement>();
+      !element.IsContentEditable() && !form_util::IsTextAreaElement(control)) {
+    return;
+  }
+  if (!base::FeatureList::IsEnabled(features::kAutofillCaretExtraction)) {
+    return;
+  }
+
+  if (!element.IsNull()) {
+    caret_state_.remove_listener = element.GetDocument().AddEventListener(
+        WebNode::EventType::kSelectionchange,
+        base::BindRepeating(&AutofillAgent::HandleCaretMovedInFormField,
+                            base::Unretained(this), element));
+  } else {
+    caret_state_.remove_listener = {};
+    caret_state_.time_of_last_event = {};
+    caret_state_.timer.Stop();
+  }
+}
+
+void AutofillAgent::HandleCaretMovedInFormField(WebElement element,
+                                                blink::WebDOMEvent) {
+  auto handle_throttled_caret_change = [](AutofillAgent& self,
+                                          WebElement element) {
+    if (!self.unsafe_render_frame() || !element.Focused() ||
+        !element.ContainsFrameSelection()) {
+      return;
+    }
+    gfx::Rect caret_bounds = GetCaretBounds(*self.unsafe_render_frame());
+    if (auto control = element.DynamicTo<WebFormControlElement>();
+        !control.IsNull()) {
+      if (std::optional<FormAndField> form_and_field =
+              FindFormAndFieldForFormControlElement(
+                  control, self.field_data_manager(),
+                  self.MaybeExtractDatalist(
+                      {form_util::ExtractOption::kBounds}))) {
+        auto& [form, field] = *form_and_field;
+        if (auto* autofill_driver = self.unsafe_autofill_driver()) {
+          autofill_driver->CaretMovedInFormField(form, field, caret_bounds);
+          return;
+        }
+      }
+    }
+    if (element.IsContentEditable()) {
+      if (std::optional<FormData> form =
+              form_util::FindFormForContentEditable(element)) {
+        CHECK_EQ(form->fields.size(), 1u);
+        if (auto* autofill_driver = self.unsafe_autofill_driver()) {
+          autofill_driver->CaretMovedInFormField(*form, form->fields.front(),
+                                                 caret_bounds);
+          return;
+        }
+      }
+    }
+  };
+  const base::Time now = base::Time::Now();
+  const base::TimeDelta time_since_last = now - caret_state_.time_of_last_event;
+  caret_state_.time_of_last_event = now;
+  if (time_since_last < base::Milliseconds(100)) {
+    caret_state_.timer.Start(FROM_HERE, base::Milliseconds(100),
+                             base::BindOnce(handle_throttled_caret_change,
+                                            std::ref(*this), element));
+  } else {
+    caret_state_.timer.Stop();
+    handle_throttled_caret_change(*this, element);
+  }
+}
+
 // AutofillAgent is deleted asynchronously because OnDestruct() may be
 // triggered by JavaScript, which in turn may be triggered by AutofillAgent.
 void AutofillAgent::OnDestruct() {
diff --git a/components/autofill/content/renderer/autofill_agent.h b/components/autofill/content/renderer/autofill_agent.h
index 83216f5..17ebbfc6 100644
--- a/components/autofill/content/renderer/autofill_agent.h
+++ b/components/autofill/content/renderer/autofill_agent.h
@@ -348,6 +348,21 @@
   void PasswordFieldReset(const blink::WebInputElement& element) override;
   void EmitFormIssuesToDevtools() override;
 
+  // Starts observing the caret in the given element. Previous observers are
+  // implicitly deleted. The event handler is HandleCaretMovedInFormField().
+  void ObserveCaret(blink::WebElement element);
+
+  // Calls CaretMovedInFormField() in a throttled manner.
+  //
+  // If HandleCaretMovedInFormField() has been called in the past 100 ms,
+  // CaretMovedInFormField() is (re-)scheduled to be fired in 100 ms. Otherwise,
+  // it is fired synchronously.
+  //
+  // Thus, the first event is fired immediately, but fast successive events are
+  // throttled until no event has been fired for 200 ms.
+  void HandleCaretMovedInFormField(blink::WebElement element,
+                                   blink::WebDOMEvent event);
+
   void HandleFocusChangeComplete(bool focused_node_was_last_clicked);
   void SendFocusedInputChangedNotificationToBrowser(
       const blink::WebElement& node);
@@ -538,6 +553,22 @@
   // manager driver mojo interface.
   FocusStateNotifier focus_state_notifier_{this};
 
+  // State for, and only for, HandleFocusChangeComplete().
+  struct Caret {
+   private:
+    friend void AutofillAgent::ObserveCaret(blink::WebElement element);
+    friend void AutofillAgent::HandleCaretMovedInFormField(
+        blink::WebElement element,
+        blink::WebDOMEvent event);
+    // Removes the caret listener from the currently observed WebElement (if
+    // any).
+    base::ScopedClosureRunner remove_listener;
+    // The last time a CaretMovedInFormField().
+    base::Time time_of_last_event;
+    // The timer for the next CaretMovedInFormField().
+    base::OneShotTimer timer;
+  } caret_state_;
+
   base::WeakPtrFactory<AutofillAgent> weak_ptr_factory_{this};
 };
 
diff --git a/components/autofill/content/renderer/autofill_agent_browsertest.cc b/components/autofill/content/renderer/autofill_agent_browsertest.cc
index e8f8d7d..3fa5cda 100644
--- a/components/autofill/content/renderer/autofill_agent_browsertest.cc
+++ b/components/autofill/content/renderer/autofill_agent_browsertest.cc
@@ -1331,6 +1331,26 @@
     task_environment_.RunUntilIdle();
   }
 
+  void SetCaret(int begin, int end, base::TimeDelta pause_for) {
+    switch (form_control_type()) {
+      case FormControlType::kContentEditable:
+        ExecuteJavaScriptForTests(base::StringPrintf(
+            R"(var c = document.getElementById('f').firstChild;
+               document.getSelection().setBaseAndExtent(c, %d, c, %d);)",
+            begin, end));
+        break;
+      case FormControlType::kInputText:
+      case FormControlType::kTextArea:
+        ExecuteJavaScriptForTests(base::StringPrintf(
+            R"(document.getElementById('f').setSelectionRange(%d, %d);)", begin,
+            end));
+        break;
+      default:
+        NOTREACHED_NORETURN();
+    }
+    task_environment_.FastForwardBy(pause_for);
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_{
       autofill::features::kAutofillCaretExtraction};
@@ -1338,8 +1358,7 @@
 
 INSTANTIATE_TEST_SUITE_P(AutofillAgentTest,
                          AutofillAgentTestCaret,
-                         ::testing::Values(FormControlType::kInputText,
-                                           FormControlType::kTextArea,
+                         ::testing::Values(FormControlType::kTextArea,
                                            FormControlType::kContentEditable));
 
 // Tests that AskForValuesToFill() is parameterized with the caret position.
@@ -1356,6 +1375,97 @@
   EXPECT_TRUE(field.bounds().Contains(gfx::RectF(caret_bounds)));
 }
 
+// Tests that CaretMovedInFormField() is fired for each caret movement, provided
+// there's enough time between the movements.
+TEST_P(AutofillAgentTestCaret, MovingCaretSlowlyFiresEvent) {
+  std::array<FormFieldData, 3> fields;
+  std::array<gfx::Rect, 3> caret_bounds;
+  testing::MockFunction<void(std::string_view)> checkpoint;
+  {
+    testing::InSequence s;
+    EXPECT_CALL(checkpoint, Call("focus"));
+    EXPECT_CALL(autofill_driver(), CaretMovedInFormField)
+        .WillOnce(DoAll(SaveArg<1>(&fields[0]), SaveArg<2>(&caret_bounds[0])));
+    EXPECT_CALL(checkpoint, Call("first move"));
+    EXPECT_CALL(autofill_driver(), CaretMovedInFormField)
+        .WillOnce(DoAll(SaveArg<1>(&fields[1]), SaveArg<2>(&caret_bounds[1])));
+    EXPECT_CALL(checkpoint, Call("second move"));
+    EXPECT_CALL(autofill_driver(), CaretMovedInFormField)
+        .WillOnce(DoAll(SaveArg<1>(&fields[2]), SaveArg<2>(&caret_bounds[2])));
+    EXPECT_CALL(checkpoint, Call("done"));
+  }
+  checkpoint.Call("focus");
+  Focus();
+  checkpoint.Call("first move");
+  SetCaret(1, 1, /*pause_for=*/base::Seconds(1));
+  checkpoint.Call("second move");
+  SetCaret(2, 2, /*pause_for=*/base::Seconds(1));
+  checkpoint.Call("done");
+  EXPECT_TRUE(fields[0].bounds().Contains(gfx::RectF(caret_bounds[0])));
+  EXPECT_TRUE(fields[1].bounds().Contains(gfx::RectF(caret_bounds[1])));
+  EXPECT_TRUE(fields[2].bounds().Contains(gfx::RectF(caret_bounds[2])));
+  EXPECT_FALSE(caret_bounds[0].origin().IsOrigin());
+  EXPECT_FALSE(caret_bounds[1].origin().IsOrigin());
+  EXPECT_FALSE(caret_bounds[2].origin().IsOrigin());
+  EXPECT_NE(caret_bounds[0], caret_bounds[1]);
+  EXPECT_NE(caret_bounds[0], caret_bounds[2]);
+  EXPECT_NE(caret_bounds[1], caret_bounds[2]);
+}
+
+// Tests that CaretMovedInFormField() is fired in a throttled manner when the
+// caret moves fast.
+TEST_P(AutofillAgentTestCaret, MovingCaretFastThrottlesEvent) {
+  testing::MockFunction<void(std::string_view)> checkpoint;
+  {
+    testing::InSequence s;
+    EXPECT_CALL(checkpoint, Call("focus"));
+    EXPECT_CALL(autofill_driver(), CaretMovedInFormField);
+    EXPECT_CALL(checkpoint, Call("first move"));
+    EXPECT_CALL(autofill_driver(), CaretMovedInFormField);
+    EXPECT_CALL(checkpoint, Call("second move is ignored"));
+    EXPECT_CALL(autofill_driver(), CaretMovedInFormField).Times(0);
+    EXPECT_CALL(checkpoint, Call("third move is throttled"));
+    EXPECT_CALL(autofill_driver(), CaretMovedInFormField).Times(0);
+    EXPECT_CALL(checkpoint, Call("timer expires"));
+    EXPECT_CALL(autofill_driver(), CaretMovedInFormField);
+    EXPECT_CALL(checkpoint, Call("done"));
+  }
+  checkpoint.Call("focus");
+  Focus();
+  checkpoint.Call("first move");
+  SetCaret(1, 1, /*pause_for=*/base::Milliseconds(1));
+  checkpoint.Call("second move is ignored");
+  SetCaret(2, 2, /*pause_for=*/base::Milliseconds(1));
+  checkpoint.Call("third move is throttled");
+  SetCaret(3, 3, /*pause_for=*/base::Milliseconds(1));
+  checkpoint.Call("timer expires");
+  task_environment_.FastForwardBy(base::Seconds(1));
+  checkpoint.Call("done");
+}
+
+// Tests that selecting text fires CaretMovedInFormField() with the text
+// selection.
+TEST_P(AutofillAgentTestCaret, SelectionFiresEvent) {
+  testing::MockFunction<void(std::string_view)> checkpoint;
+  {
+    testing::InSequence s;
+    EXPECT_CALL(checkpoint, Call("focus"));
+    EXPECT_CALL(autofill_driver(),
+                CaretMovedInFormField(
+                    _, Property(&FormFieldData::selected_text, u""), _));
+    EXPECT_CALL(checkpoint, Call("selection"));
+    EXPECT_CALL(autofill_driver(),
+                CaretMovedInFormField(
+                    _, Property(&FormFieldData::selected_text, u"123"), _));
+    EXPECT_CALL(checkpoint, Call("done"));
+  }
+  checkpoint.Call("focus");
+  Focus();
+  checkpoint.Call("selection");
+  SetCaret(1, 4, /*pause_for=*/base::Seconds(1));
+  checkpoint.Call("done");
+}
+
 }  // namespace
 
 }  // namespace autofill
diff --git a/components/autofill/content/renderer/autofill_renderer_test.h b/components/autofill/content/renderer/autofill_renderer_test.h
index d50d588..a8858f5 100644
--- a/components/autofill/content/renderer/autofill_renderer_test.h
+++ b/components/autofill/content/renderer/autofill_renderer_test.h
@@ -48,6 +48,12 @@
                mojom::SubmissionSource source),
               (override));
   MOCK_METHOD(void,
+              CaretMovedInFormField,
+              (const FormData& form,
+               const FormFieldData& field,
+               const gfx::Rect& caret_bounds),
+              (override));
+  MOCK_METHOD(void,
               TextFieldDidChange,
               (const FormData& form,
                const FormFieldData& field,
diff --git a/components/autofill/core/browser/autofill_driver_router.cc b/components/autofill/core/browser/autofill_driver_router.cc
index 7d1c8381..b07dd5bc 100644
--- a/components/autofill/core/browser/autofill_driver_router.cc
+++ b/components/autofill/core/browser/autofill_driver_router.cc
@@ -177,6 +177,21 @@
   callback(CHECK_DEREF(target), browser_form, known_success, submission_source);
 }
 
+void AutofillDriverRouter::CaretMovedInFormField(
+    AutofillDriver& source,
+    FormData form,
+    const FormFieldData& field,
+    const gfx::Rect& caret_bounds,
+    RoutedCallback<const FormData&, const FormFieldData&, const gfx::Rect&>
+        callback) {
+  FormGlobalId form_id = form.global_id();
+  form_forest_.UpdateTreeOfRendererForm(std::move(form), source);
+
+  const FormData& browser_form = form_forest_.GetBrowserForm(form_id);
+  auto* target = DriverOfFrame(browser_form.host_frame);
+  callback(CHECK_DEREF(target), browser_form, field, caret_bounds);
+}
+
 void AutofillDriverRouter::TextFieldDidChange(
     AutofillDriver& source,
     FormData form,
diff --git a/components/autofill/core/browser/autofill_driver_router.h b/components/autofill/core/browser/autofill_driver_router.h
index 2a1dd9a..1743588 100644
--- a/components/autofill/core/browser/autofill_driver_router.h
+++ b/components/autofill/core/browser/autofill_driver_router.h
@@ -166,6 +166,13 @@
       bool known_success,
       mojom::SubmissionSource submission_source,
       RoutedCallback<const FormData&, bool, mojom::SubmissionSource> callback);
+  void CaretMovedInFormField(
+      AutofillDriver& source,
+      FormData form,
+      const FormFieldData& field,
+      const gfx::Rect& caret_bounds,
+      RoutedCallback<const FormData&, const FormFieldData&, const gfx::Rect&>
+          callback);
   void TextFieldDidChange(
       AutofillDriver& source,
       FormData form,
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index e04fce63..a46bcdf 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -359,6 +359,22 @@
   }
 }
 
+void AutofillManager::OnCaretMovedInFormField(const FormData& form,
+                                              const FormFieldData& field,
+                                              const gfx::Rect& caret_bounds) {
+  if (!IsValidFormData(form)) {
+    return;
+  }
+  NotifyObservers(&Observer::OnBeforeCaretMovedInFormField, form.global_id(),
+                  field.global_id(), caret_bounds);
+  ParseFormAsync(
+      form, ParsingCallback(&AutofillManager::OnCaretMovedInFormFieldImpl,
+                            field, caret_bounds)
+                .Then(NotifyObserversCallback(
+                    &Observer::OnAfterCaretMovedInFormField, form.global_id(),
+                    field.global_id(), caret_bounds)));
+}
+
 void AutofillManager::OnTextFieldDidChange(const FormData& form,
                                            const FormFieldData& field,
                                            const base::TimeTicks timestamp) {
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 192b152..9587dda 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -86,6 +86,15 @@
     virtual void OnAfterFormsSeen(AutofillManager& manager,
                                   base::span<const FormGlobalId> forms) {}
 
+    virtual void OnBeforeCaretMovedInFormField(AutofillManager& manager,
+                                               const FormGlobalId& form,
+                                               const FieldGlobalId& field,
+                                               const gfx::Rect& caret_bounds) {}
+    virtual void OnAfterCaretMovedInFormField(AutofillManager& manager,
+                                              const FormGlobalId& form,
+                                              const FieldGlobalId& field,
+                                              const gfx::Rect& caret_bounds) {}
+
     virtual void OnBeforeTextFieldDidChange(AutofillManager& manager,
                                             FormGlobalId form,
                                             FieldGlobalId field) {}
@@ -229,6 +238,9 @@
       const gfx::Rect& caret_bounds,
       AutofillSuggestionTriggerSource trigger_source);
   void OnHidePopup();
+  virtual void OnCaretMovedInFormField(const FormData& form,
+                                       const FormFieldData& field,
+                                       const gfx::Rect& caret_bounds);
   virtual void OnDidFillAutofillFormData(const FormData& form,
                                          const base::TimeTicks timestamp);
   virtual void OnJavaScriptChangedAutofilledValue(
@@ -323,6 +335,9 @@
   virtual void OnFormSubmittedImpl(const FormData& form,
                                    bool known_success,
                                    mojom::SubmissionSource source) = 0;
+  virtual void OnCaretMovedInFormFieldImpl(const FormData& form,
+                                           const FormFieldData& field,
+                                           const gfx::Rect& caret_bounds) = 0;
   virtual void OnTextFieldDidChangeImpl(const FormData& form,
                                         const FormFieldData& field,
                                         const base::TimeTicks timestamp) = 0;
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index a1cd398..bd47ce3 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -26,6 +26,7 @@
 #include "components/autofill/core/browser/data_model/credit_card_test_api.h"
 #include "components/autofill/core/browser/data_model/iban.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
 #include "components/autofill/core/browser/payments_data_manager.h"
 #include "components/autofill/core/browser/randomized_encoder.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
@@ -609,7 +610,11 @@
         challenge_option.id =
             CardUnmaskChallengeOption::ChallengeOptionId("456");
         challenge_option.type = type;
-        challenge_option.url_to_open = GURL("https://www.example.com");
+        Vcn3dsChallengeOptionMetadata metadata;
+        metadata.url_to_open = GURL("https://www.example.com");
+        metadata.success_query_param_name = "token";
+        metadata.failure_query_param_name = "failure";
+        challenge_option.vcn_3ds_metadata = std::move(metadata);
         challenge_options.emplace_back(std::move(challenge_option));
         break;
       }
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index 73c4bdc..a486e8c 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -419,6 +419,9 @@
   void OnFormSubmittedImpl(const FormData& form,
                            bool known_success,
                            mojom::SubmissionSource source) override;
+  void OnCaretMovedInFormFieldImpl(const FormData& form,
+                                   const FormFieldData& field,
+                                   const gfx::Rect& caret_bounds) override {}
   void OnTextFieldDidChangeImpl(const FormData& form,
                                 const FormFieldData& field,
                                 const base::TimeTicks timestamp) override;
diff --git a/components/autofill/core/browser/mock_autofill_manager.h b/components/autofill/core/browser/mock_autofill_manager.h
index c9e29f9e..785aaf6 100644
--- a/components/autofill/core/browser/mock_autofill_manager.h
+++ b/components/autofill/core/browser/mock_autofill_manager.h
@@ -59,6 +59,12 @@
                mojom::SubmissionSource source),
               (override));
   MOCK_METHOD(void,
+              OnCaretMovedInFormFieldImpl,
+              (const FormData& form,
+               const FormFieldData& field,
+               const gfx::Rect& caret_bounds),
+              (override));
+  MOCK_METHOD(void,
               OnTextFieldDidChangeImpl,
               (const FormData& form,
                const FormFieldData& field,
diff --git a/components/autofill/core/browser/payments/card_unmask_challenge_option.cc b/components/autofill/core/browser/payments/card_unmask_challenge_option.cc
index 788073c..263b1d7 100644
--- a/components/autofill/core/browser/payments/card_unmask_challenge_option.cc
+++ b/components/autofill/core/browser/payments/card_unmask_challenge_option.cc
@@ -6,6 +6,22 @@
 
 namespace autofill {
 
+Vcn3dsChallengeOptionMetadata::Vcn3dsChallengeOptionMetadata() = default;
+
+Vcn3dsChallengeOptionMetadata::Vcn3dsChallengeOptionMetadata(
+    const Vcn3dsChallengeOptionMetadata&) = default;
+
+Vcn3dsChallengeOptionMetadata::Vcn3dsChallengeOptionMetadata(
+    Vcn3dsChallengeOptionMetadata&&) = default;
+
+Vcn3dsChallengeOptionMetadata& Vcn3dsChallengeOptionMetadata::operator=(
+    const Vcn3dsChallengeOptionMetadata&) = default;
+
+Vcn3dsChallengeOptionMetadata& Vcn3dsChallengeOptionMetadata::operator=(
+    Vcn3dsChallengeOptionMetadata&&) = default;
+
+Vcn3dsChallengeOptionMetadata::~Vcn3dsChallengeOptionMetadata() = default;
+
 CardUnmaskChallengeOption::CardUnmaskChallengeOption(
     ChallengeOptionId id,
     CardUnmaskChallengeOptionType type,
diff --git a/components/autofill/core/browser/payments/card_unmask_challenge_option.h b/components/autofill/core/browser/payments/card_unmask_challenge_option.h
index 60082b1..ac712e5c 100644
--- a/components/autofill/core/browser/payments/card_unmask_challenge_option.h
+++ b/components/autofill/core/browser/payments/card_unmask_challenge_option.h
@@ -41,6 +41,32 @@
   kMaxValue = kBackOfCard,
 };
 
+// Metadata from the server related to a VCN 3DS challenge option.
+struct Vcn3dsChallengeOptionMetadata {
+  Vcn3dsChallengeOptionMetadata();
+  Vcn3dsChallengeOptionMetadata(const Vcn3dsChallengeOptionMetadata&);
+  Vcn3dsChallengeOptionMetadata(Vcn3dsChallengeOptionMetadata&&);
+  Vcn3dsChallengeOptionMetadata& operator=(
+      const Vcn3dsChallengeOptionMetadata&);
+  Vcn3dsChallengeOptionMetadata& operator=(Vcn3dsChallengeOptionMetadata&&);
+  ~Vcn3dsChallengeOptionMetadata();
+
+  // URL that will be opened in the VCN 3DS pop-up.
+  GURL url_to_open;
+
+  // Name of the success query param. On every navigation inside of a VCN 3DS
+  // pop-up, this query param will be searched for. If found, it signals to
+  // Chrome that the authentication inside of the pop-up was successful. The
+  // value of the query param will be the context token that must be sent back
+  // to the server in the second UnmaskCardRequest call.
+  std::string success_query_param_name;
+
+  // Name of the failure query param. On every navigation inside of a VCN 3DS
+  // pop-up, this query param will be searched for. If found, it signals to
+  // Chrome that the authentication inside of the pop-up was a failure.
+  std::string failure_query_param_name;
+};
+
 // The struct used by Autofill components to represent a card unmask challenge
 // option. User must select a challenge option to unmask their credit card.
 // Currently, only CVC and SMS OTP are supported.
@@ -71,8 +97,9 @@
   // option, such as the masked phone number that will receive an SMS, etc.
   std::u16string challenge_info = std::u16string();
 
-  // Used for challenge options that need to open a URL when initiated.
-  GURL url_to_open;
+  // Only present if `type` is
+  // `CardUnmaskChallengeOptionType::kThreeDomainSecure`.
+  std::optional<Vcn3dsChallengeOptionMetadata> vcn_3ds_metadata;
 
   // The predetermined length of the input of the challenge.
   size_t challenge_input_length = 0U;
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index ee5fb99..9cdbe941 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/check_deref.h"
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/ranges/algorithm.h"
@@ -52,13 +53,14 @@
 namespace autofill {
 
 namespace {
-// Timeout to wait for unmask details from Google Payments in milliseconds.
-constexpr int64_t kUnmaskDetailsResponseTimeoutMs = 3 * 1000;  // 3 sec
+
+// Timeout to wait for unmask details from Google Payments.
+constexpr auto kUnmaskDetailsResponseTimeout = base::Seconds(3);
 // Time to wait between multiple calls to GetUnmaskDetails().
-constexpr int64_t kDelayForGetUnmaskDetails = 3 * 60 * 1000;  // 3 min
+constexpr auto kDelayForGetUnmaskDetails = base::Minutes(3);
 
 // Suffix for server IDs in the cache indicating that a card is a virtual card.
-const char kVirtualCardIdentifier[] = "_vcn";
+constexpr char kVirtualCardIdentifier[] = "_vcn";
 
 }  // namespace
 
@@ -67,8 +69,8 @@
     AutofillClient* client,
     PersonalDataManager* personal_data_manager,
     autofill_metrics::CreditCardFormEventLogger* form_event_logger)
-    : driver_(driver),
-      client_(client),
+    : driver_(CHECK_DEREF(driver)),
+      client_(CHECK_DEREF(client)),
       personal_data_manager_(personal_data_manager),
       form_event_logger_(form_event_logger) {}
 
@@ -77,13 +79,11 @@
   // interactive authentication flow upon page navigation, as page navigation
   // results in us destroying the current CreditCardAccessManager and creating a
   // new one.
-  if (client_) {
     if (auto* form_data_importer = client_->GetFormDataImporter()) {
       form_data_importer
           ->SetPaymentMethodTypeIfNonInteractiveAuthenticationFlowCompleted(
               std::nullopt);
     }
-  }
 }
 
 void CreditCardAccessManager::UpdateCreditCardFormEventLogger() {
@@ -155,7 +155,7 @@
 
     GetOrCreateFidoAuthenticator()->IsUserVerifiable(base::BindOnce(
         &CreditCardAccessManager::GetUnmaskDetailsIfUserIsVerifiable,
-        weak_ptr_factory_.GetWeakPtr()));
+        GetWeakPtr()));
   }
 #endif
 }
@@ -185,12 +185,10 @@
   if (is_user_verifiable_.value_or(false)) {
     unmask_details_request_in_progress_ = true;
     preflight_call_timestamp_ = base::TimeTicks::Now();
-    client_->GetPaymentsAutofillClient()
-        ->GetPaymentsNetworkInterface()
-        ->GetUnmaskDetails(
-            base::BindOnce(&CreditCardAccessManager::OnDidGetUnmaskDetails,
-                           weak_ptr_factory_.GetWeakPtr()),
-            personal_data_manager_->app_locale());
+    payments_autofill_client()->GetPaymentsNetworkInterface()->GetUnmaskDetails(
+        base::BindOnce(&CreditCardAccessManager::OnDidGetUnmaskDetails,
+                       GetWeakPtr()),
+        personal_data_manager_->app_locale());
     autofill_metrics::LogCardUnmaskPreflightCalled(
         GetOrCreateFidoAuthenticator()->IsUserOptedIn());
   }
@@ -260,12 +258,11 @@
       unmask_details_.offer_fido_opt_in && !client_->IsOffTheRecord();
 
   // Set delay as fido request timeout if available, otherwise set to default.
-  int delay_ms = kDelayForGetUnmaskDetails;
+  base::TimeDelta delay = kDelayForGetUnmaskDetails;
   if (unmask_details_.fido_request_options.has_value()) {
-    const std::optional<int> request_timeout =
-        unmask_details_.fido_request_options->FindInt("timeout_millis");
-    if (request_timeout.has_value()) {
-      delay_ms = *request_timeout;
+    if (std::optional<int> request_timeout =
+            unmask_details_.fido_request_options->FindInt("timeout_millis")) {
+      delay = base::Milliseconds(*request_timeout);
     }
   }
 
@@ -280,8 +277,8 @@
   base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&CreditCardAccessManager::SignalCanFetchUnmaskDetails,
-                     weak_ptr_factory_.GetWeakPtr()),
-      base::Milliseconds(delay_ms));
+                     GetWeakPtr()),
+      delay);
 }
 
 void CreditCardAccessManager::FetchCreditCard(
@@ -458,7 +455,7 @@
   if (challenge_options.empty()) {
     std::move(on_credit_card_fetched_callback_)
         .Run(CreditCardFetchResult::kTransientError, nullptr);
-    client_->GetPaymentsAutofillClient()->ShowAutofillErrorDialog(
+    payments_autofill_client()->ShowAutofillErrorDialog(
         AutofillErrorDialogContext::WithVirtualCardPermanentOrTemporaryError(
             /*is_permanent_error=*/true));
     Reset();
@@ -581,8 +578,7 @@
         }
       }
       GetOrCreateFidoAuthenticator()->Authenticate(
-          *card_, weak_ptr_factory_.GetWeakPtr(),
-          std::move(fido_request_options), context_token);
+          *card_, GetWeakPtr(), std::move(fido_request_options), context_token);
 #endif
       break;
     }
@@ -603,24 +599,18 @@
       // the vcn context token and the selected challenge option.
       if (card_->record_type() == CreditCard::RecordType::kVirtualCard) {
         DCHECK(selected_challenge_option_);
-        client_->GetPaymentsAutofillClient()
-            ->GetCvcAuthenticator()
-            .Authenticate(card_.get(), weak_ptr_factory_.GetWeakPtr(),
-                          personal_data_manager_,
-                          virtual_card_unmask_response_details_.context_token,
-                          *selected_challenge_option_);
+        payments_autofill_client()->GetCvcAuthenticator().Authenticate(
+            card_.get(), GetWeakPtr(), personal_data_manager_,
+            virtual_card_unmask_response_details_.context_token,
+            *selected_challenge_option_);
       } else if (IsMaskedServerCardRiskBasedAuthAvailable()) {
         CHECK(!risk_based_authentication_response_.context_token.empty());
-        client_->GetPaymentsAutofillClient()
-            ->GetCvcAuthenticator()
-            .Authenticate(card_.get(), weak_ptr_factory_.GetWeakPtr(),
-                          personal_data_manager_,
-                          risk_based_authentication_response_.context_token);
+        payments_autofill_client()->GetCvcAuthenticator().Authenticate(
+            card_.get(), GetWeakPtr(), personal_data_manager_,
+            risk_based_authentication_response_.context_token);
       } else {
-        client_->GetPaymentsAutofillClient()
-            ->GetCvcAuthenticator()
-            .Authenticate(card_.get(), weak_ptr_factory_.GetWeakPtr(),
-                          personal_data_manager_);
+        payments_autofill_client()->GetCvcAuthenticator().Authenticate(
+            card_.get(), GetWeakPtr(), personal_data_manager_);
       }
       break;
     }
@@ -628,11 +618,10 @@
     case UnmaskAuthFlowType::kOtpFallbackFromFido: {
       // Delegate the task to CreditCardOtpAuthenticator.
       DCHECK(selected_challenge_option_);
-      client_->GetPaymentsAutofillClient()
+      payments_autofill_client()
           ->GetOtpAuthenticator()
           ->OnChallengeOptionSelected(
-              card_.get(), *selected_challenge_option_,
-              weak_ptr_factory_.GetWeakPtr(),
+              card_.get(), *selected_challenge_option_, GetWeakPtr(),
               virtual_card_unmask_response_details_.context_token,
               payments::GetBillingCustomerId(
                   &personal_data_manager_->payments_data_manager()));
@@ -649,11 +638,11 @@
       vcn_3ds_context.challenge_option = *selected_challenge_option_;
       vcn_3ds_context.completion_callback = base::BindOnce(
           &CreditCardAccessManager::OnVcn3dsAuthenticationComplete,
-          weak_ptr_factory_.GetWeakPtr());
+          GetWeakPtr());
       vcn_3ds_context.user_consent_already_given =
           unmask_auth_flow_type_ ==
           UnmaskAuthFlowType::kThreeDomainSecureConsentAlreadyGiven;
-      client_->GetPaymentsAutofillClient()
+      payments_autofill_client()
           ->GetPaymentsWindowManager()
           ->InitVcn3dsAuthentication(std::move(vcn_3ds_context));
       break;
@@ -670,8 +659,8 @@
 CreditCardFidoAuthenticator*
 CreditCardAccessManager::GetOrCreateFidoAuthenticator() {
   if (!fido_authenticator_)
-    fido_authenticator_ =
-        std::make_unique<CreditCardFidoAuthenticator>(driver_, client_);
+    fido_authenticator_ = std::make_unique<CreditCardFidoAuthenticator>(
+        &driver_.get(), &client_.get());
   return fido_authenticator_.get();
 }
 #endif
@@ -728,7 +717,7 @@
 
     // Additionally authorizes the card with FIDO. It also delays the form
     // filling.
-    GetOrCreateFidoAuthenticator()->Authorize(weak_ptr_factory_.GetWeakPtr(),
+    GetOrCreateFidoAuthenticator()->Authorize(GetWeakPtr(),
                                               response.card_authorization_token,
                                               request_options->Clone());
 #endif
@@ -805,7 +794,7 @@
           features::kAutofillEnableFIDOProgressDialog)) {
     // Close the progress dialog when the authentication for getting the full
     // card completes.
-    client_->GetPaymentsAutofillClient()->CloseAutofillProgressDialog(
+    payments_autofill_client()->CloseAutofillProgressDialog(
         /*show_confirmation_before_closing=*/true,
         /*no_interactive_authentication_callback=*/base::OnceClosure());
   }
@@ -836,7 +825,7 @@
     // If it is an virtual card retrieval error, we don't want to invoke the CVC
     // authentication afterwards. Instead reset all states, notify accessor and
     // invoke the error dialog.
-    client_->GetPaymentsAutofillClient()->ShowAutofillErrorDialog(
+    payments_autofill_client()->ShowAutofillErrorDialog(
         AutofillErrorDialogContext::WithVirtualCardPermanentOrTemporaryError(
             /*is_permanent_error=*/response.failure_type ==
             payments::FullCardRequest::
@@ -1075,17 +1064,15 @@
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
   GetOrCreateFidoAuthenticator()->OnWebauthnOfferDialogRequested(
       card_authorization_token);
-  client_->ShowWebauthnOfferDialog(
-      base::BindRepeating(&CreditCardAccessManager::HandleDialogUserResponse,
-                          weak_ptr_factory_.GetWeakPtr()));
+  client_->ShowWebauthnOfferDialog(base::BindRepeating(
+      &CreditCardAccessManager::HandleDialogUserResponse, GetWeakPtr()));
 #endif
 }
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 void CreditCardAccessManager::ShowVerifyPendingDialog() {
-  client_->ShowWebauthnVerifyPendingDialog(
-      base::BindRepeating(&CreditCardAccessManager::HandleDialogUserResponse,
-                          weak_ptr_factory_.GetWeakPtr()));
+  client_->ShowWebauthnVerifyPendingDialog(base::BindRepeating(
+      &CreditCardAccessManager::HandleDialogUserResponse, GetWeakPtr()));
 }
 
 void CreditCardAccessManager::HandleDialogUserResponse(
@@ -1102,7 +1089,7 @@
     case WebauthnDialogCallbackType::kVerificationCancelled:
       // TODO(crbug.com/40621544): Add tests and logging for canceling verify
       // pending dialog.
-      client_->GetPaymentsAutofillClient()
+      payments_autofill_client()
           ->GetPaymentsNetworkInterface()
           ->CancelRequest();
       SignalCanFetchUnmaskDetails();
@@ -1151,19 +1138,16 @@
     }
 #endif
 
-    client_->GetPaymentsAutofillClient()->ShowAutofillProgressDialog(
+    payments_autofill_client()->ShowAutofillProgressDialog(
         AutofillProgressDialogType::kServerCardUnmaskProgressDialog,
         /*cancel_callback=*/base::BindOnce(
             &CreditCardRiskBasedAuthenticator::OnUnmaskCancelled,
-            client_->GetPaymentsAutofillClient()
+            payments_autofill_client()
                 ->GetRiskBasedAuthenticator()
                 ->AsWeakPtr()));
 
-    // TODO(crbug.com/1375748): Reduce the number of calls to
-    // `client_->GetPaymentsAutofillClient()`.
-    client_->GetPaymentsAutofillClient()
-        ->GetRiskBasedAuthenticator()
-        ->Authenticate(*card_, weak_ptr_factory_.GetWeakPtr());
+    payments_autofill_client()->GetRiskBasedAuthenticator()->Authenticate(
+        *card_, GetWeakPtr());
     // Risk-based authentication is handled in CreditCardRiskBasedAuthenticator.
     // Further delegation will be handled in
     // CreditCardAccessManager::OnRiskBasedAuthenticationResponseReceived.
@@ -1203,8 +1187,8 @@
     // OnStopWaitingForUnmaskDetails().
     ready_to_start_authentication_.OnEventOrTimeOut(
         base::BindOnce(&CreditCardAccessManager::OnStopWaitingForUnmaskDetails,
-                       weak_ptr_factory_.GetWeakPtr()),
-        base::Milliseconds(kUnmaskDetailsResponseTimeoutMs));
+                       GetWeakPtr()),
+        kUnmaskDetailsResponseTimeout);
   } else {
     StartAuthenticationFlow(
         IsFidoAuthEnabled(get_unmask_details_returned &&
@@ -1215,13 +1199,12 @@
 
 void CreditCardAccessManager::FetchVirtualCard() {
   is_authentication_in_progress_ = true;
-  client_->GetPaymentsAutofillClient()->ShowAutofillProgressDialog(
+  payments_autofill_client()->ShowAutofillProgressDialog(
       AutofillProgressDialogType::kVirtualCardUnmaskProgressDialog,
       base::BindOnce(&CreditCardAccessManager::OnVirtualCardUnmaskCancelled,
-                     weak_ptr_factory_.GetWeakPtr()));
-  client_->GetPaymentsAutofillClient()
-      ->GetRiskBasedAuthenticator()
-      ->Authenticate(*card_, weak_ptr_factory_.GetWeakPtr());
+                     GetWeakPtr()));
+  payments_autofill_client()->GetRiskBasedAuthenticator()->Authenticate(
+      *card_, GetWeakPtr());
 }
 
 void CreditCardAccessManager::FetchLocalOrFullServerCard() {
@@ -1291,7 +1274,7 @@
         Result::kAuthenticationRequired:
       // Authenticates users to unmask the card if the response indicates
       // further authentication is required.
-      client_->GetPaymentsAutofillClient()->CloseAutofillProgressDialog(
+      payments_autofill_client()->CloseAutofillProgressDialog(
           /*show_confirmation_before_closing=*/false,
           /*no_interactive_authentication_callback=*/base::OnceClosure());
       CHECK(!response.context_token.empty());
@@ -1320,12 +1303,12 @@
     case CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse::
         Result::kError:
       // Shows error dialog to users if the authentication failed.
-      client_->GetPaymentsAutofillClient()->CloseAutofillProgressDialog(
+      payments_autofill_client()->CloseAutofillProgressDialog(
           /*show_confirmation_before_closing=*/false,
           /*no_interactive_authentication_callback=*/base::OnceClosure());
       std::move(on_credit_card_fetched_callback_)
           .Run(CreditCardFetchResult::kTransientError, nullptr);
-      client_->GetPaymentsAutofillClient()->ShowAutofillErrorDialog(
+      payments_autofill_client()->ShowAutofillErrorDialog(
           response.error_dialog_context);
 
       autofill_metrics::LogServerCardUnmaskResult(
@@ -1373,7 +1356,7 @@
     // Otherwise further authentication is required to unmask the card.
     DCHECK(!response_details.context_token.empty());
     // Close the progress dialog without showing the confirmation.
-    client_->GetPaymentsAutofillClient()->CloseAutofillProgressDialog(
+    payments_autofill_client()->CloseAutofillProgressDialog(
         /*show_confirmation_before_closing=*/false,
         /*no_interactive_authentication_callback=*/base::OnceClosure());
     StartAuthenticationFlow(
@@ -1386,7 +1369,7 @@
   // permanent error dialog, and for all other cases we show VCN temporary
   // error dialog.
   // Close the progress dialog without showing the confirmation.
-  client_->GetPaymentsAutofillClient()->CloseAutofillProgressDialog(
+  payments_autofill_client()->CloseAutofillProgressDialog(
       /*show_confirmation_before_closing=*/false,
       /*no_interactive_authentication_callback=*/base::OnceClosure());
   std::move(on_credit_card_fetched_callback_)
@@ -1416,10 +1399,10 @@
     // Error fields returned in the server response are more detailed than the
     // virtual card temporary/permanent error messages stored on the client, so
     // prefer the server-returned fields if they exist.
-    client_->GetPaymentsAutofillClient()->ShowAutofillErrorDialog(
+    payments_autofill_client()->ShowAutofillErrorDialog(
         *response_details.autofill_error_dialog_context);
   } else {
-    client_->GetPaymentsAutofillClient()->ShowAutofillErrorDialog(
+    payments_autofill_client()->ShowAutofillErrorDialog(
         AutofillErrorDialogContext::WithVirtualCardPermanentOrTemporaryError(
             /*is_permanent_error=*/result ==
             AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure));
@@ -1436,7 +1419,7 @@
     // the prompt after the progress dialog has been closed, which we can do
     // by using the `no_interactive_authentication_callback` parameter in
     // `PaymentsAutofillClient::CloseAutofillProgressDialog()`.
-    client_->GetPaymentsAutofillClient()->CloseAutofillProgressDialog(
+    payments_autofill_client()->CloseAutofillProgressDialog(
         /*show_confirmation_before_closing=*/false,
         /*no_interactive_authentication_callback=*/base::BindOnce(
             // `StartDeviceAuthenticationForFilling()` will asynchronously
@@ -1444,9 +1427,9 @@
             // calling `Reset()` until the re-authentication flow is
             // complete.
             &CreditCardAccessManager::StartDeviceAuthenticationForFilling,
-            weak_ptr_factory_.GetWeakPtr(), card_.get()));
+            GetWeakPtr(), card_.get()));
   } else {
-    client_->GetPaymentsAutofillClient()->CloseAutofillProgressDialog(
+    payments_autofill_client()->CloseAutofillProgressDialog(
         /*show_confirmation_before_closing=*/true,
         /*no_interactive_authentication_callback=*/base::OnceClosure());
     std::move(on_credit_card_fetched_callback_)
@@ -1519,7 +1502,7 @@
     NOTREACHED_IN_MIGRATION();
     std::move(on_credit_card_fetched_callback_)
         .Run(CreditCardFetchResult::kTransientError, nullptr);
-    client_->GetPaymentsAutofillClient()->ShowAutofillErrorDialog(
+    payments_autofill_client()->ShowAutofillErrorDialog(
         AutofillErrorDialogContext::WithVirtualCardPermanentOrTemporaryError(
             /*is_permanent_error=*/false));
     Reset();
@@ -1559,7 +1542,7 @@
     // Virtual Card Unmask request, so we need to reset the state of the
     // CreditCardOtpAuthenticator as well to ensure the flow does not continue,
     // as continuing the flow can cause a crash.
-    client_->GetPaymentsAutofillClient()->GetOtpAuthenticator()->Reset();
+    payments_autofill_client()->GetOtpAuthenticator()->Reset();
   }
 
   autofill_metrics::ServerCardUnmaskFlowType flow_type;
@@ -1631,13 +1614,13 @@
 }
 
 void CreditCardAccessManager::ShowUnmaskAuthenticatorSelectionDialog() {
-  client_->GetPaymentsAutofillClient()->ShowUnmaskAuthenticatorSelectionDialog(
+  payments_autofill_client()->ShowUnmaskAuthenticatorSelectionDialog(
       virtual_card_unmask_response_details_.card_unmask_challenge_options,
       base::BindOnce(
           &CreditCardAccessManager::OnUserAcceptedAuthenticationSelectionDialog,
-          weak_ptr_factory_.GetWeakPtr()),
+          GetWeakPtr()),
       base::BindOnce(&CreditCardAccessManager::OnVirtualCardUnmaskCancelled,
-                     weak_ptr_factory_.GetWeakPtr()));
+                     GetWeakPtr()));
 }
 
 CardUnmaskChallengeOption*
@@ -1713,7 +1696,7 @@
       l10n_util::GetStringUTF16(IDS_PAYMENTS_AUTOFILL_FILLING_MANDATORY_REAUTH),
       base::BindOnce(
           &CreditCardAccessManager::OnDeviceAuthenticationResponseForFilling,
-          weak_ptr_factory_.GetWeakPtr(), authentication_method, card));
+          GetWeakPtr(), authentication_method, card));
 #elif BUILDFLAG(IS_ANDROID)
   // TODO(crbug.com/40261690): Convert this to
   // MandatoryReauthManager::AuthenticateWithMessage() with the correct message
@@ -1721,7 +1704,7 @@
   client_->GetOrCreatePaymentsMandatoryReauthManager()->Authenticate(
       base::BindOnce(
           &CreditCardAccessManager::OnDeviceAuthenticationResponseForFilling,
-          weak_ptr_factory_.GetWeakPtr(), authentication_method, card));
+          GetWeakPtr(), authentication_method, card));
 #else
   NOTREACHED_NORETURN();
 #endif
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.h b/components/autofill/core/browser/payments/credit_card_access_manager.h
index 63e69a9c..dd06c16 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.h
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.h
@@ -183,7 +183,16 @@
  private:
   friend class CreditCardAccessManagerTestApi;
 
-  // Returns whether or not unmasked card cache is empty. Exposed for testing.
+  payments::PaymentsAutofillClient* payments_autofill_client() {
+    return client_->GetPaymentsAutofillClient();
+  }
+
+  base::WeakPtr<CreditCardAccessManager> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+  // Returns whether or not unmasked card cache is empty. Exposed for
+  // testing.
   bool UnmaskedCardCacheIsEmpty();
 
   // Invoked from CreditCardFidoAuthenticator::IsUserVerifiable().
@@ -374,15 +383,14 @@
   // OnCvcAuthenticationComplete() to be executed.
   bool is_authentication_in_progress_ = false;
 
-  // The associated autofill driver. Weak reference.
-  const raw_ptr<AutofillDriver> driver_;
+  // The associated autofill driver.
+  const raw_ref<AutofillDriver> driver_;
 
-  // The associated autofill client. Weak reference.
-  const raw_ptr<AutofillClient> client_;
+  // The associated autofill client.
+  const raw_ref<AutofillClient> client_;
 
   // The personal data manager, used to save and load personal data to/from the
   // web database.
-  // Weak reference.
   const raw_ptr<PersonalDataManager> personal_data_manager_;
 
   // For logging metrics.
diff --git a/components/autofill/core/browser/payments/payments_network_interface.cc b/components/autofill/core/browser/payments/payments_network_interface.cc
index 3abdf639..1e59ad5e 100644
--- a/components/autofill/core/browser/payments/payments_network_interface.cc
+++ b/components/autofill/core/browser/payments/payments_network_interface.cc
@@ -98,7 +98,7 @@
   merchant_domain_for_footprints = other.merchant_domain_for_footprints;
   selected_challenge_option = other.selected_challenge_option;
   client_behavior_signals = other.client_behavior_signals;
-  redirect_completion_proof = other.redirect_completion_proof;
+  redirect_completion_result = other.redirect_completion_result;
   return *this;
 }
 
diff --git a/components/autofill/core/browser/payments/payments_network_interface.h b/components/autofill/core/browser/payments/payments_network_interface.h
index 835d382..0ece929 100644
--- a/components/autofill/core/browser/payments/payments_network_interface.h
+++ b/components/autofill/core/browser/payments/payments_network_interface.h
@@ -136,7 +136,7 @@
     std::optional<url::Origin> merchant_domain_for_footprints;
     // The token received in the final redirect of a PaymentsWindowManager flow,
     // which is the only scenario where this field should be populated.
-    PaymentsWindowManager::RedirectCompletionProof redirect_completion_proof;
+    PaymentsWindowManager::RedirectCompletionResult redirect_completion_result;
   };
 
   // Information retrieved from an UnmaskRequest.
diff --git a/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc b/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
index 8886b44..770fdba 100644
--- a/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
+++ b/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
@@ -56,11 +56,27 @@
         CardUnmaskChallengeOption::ChallengeOptionId(*challenge_id);
   }
 
-  const auto* url_to_open = defined_challenge_option.FindString("redirect_url");
-  if (url_to_open) {
-    parsed_challenge_option.url_to_open = GURL(*url_to_open);
+  Vcn3dsChallengeOptionMetadata metadata;
+  if (const auto* url_to_open =
+          defined_challenge_option.FindString("popup_url")) {
+    metadata.url_to_open = GURL(*url_to_open);
   }
 
+  if (const auto* query_params =
+          defined_challenge_option.FindDict("query_params_for_popup_close")) {
+    if (const auto* success_query_param_name =
+            query_params->FindString("success_query_param_name")) {
+      metadata.success_query_param_name = *success_query_param_name;
+    }
+
+    if (const auto* failure_query_param_name =
+            query_params->FindString("failure_query_param_name")) {
+      metadata.failure_query_param_name = *failure_query_param_name;
+    }
+  }
+
+  parsed_challenge_option.vcn_3ds_metadata = std::move(metadata);
+
   parsed_challenge_option.challenge_info = l10n_util::GetStringUTF16(
       IDS_AUTOFILL_CARD_UNMASK_AUTHENTICATION_SELECTION_DIALOG_THREE_DOMAIN_SECURE_CHALLENGE_INFO);
 }
@@ -196,7 +212,7 @@
   // `defined_challenge_option` to the defined challenge option found, parse the
   // challenge option, and return it.
   else if ((defined_challenge_option =
-                challenge_option.FindDict("redirect_challenge_option")) &&
+                challenge_option.FindDict("popup_challenge_option")) &&
            IsVcn3dsEnabled()) {
     ParseAs3dsChallengeOption(*defined_challenge_option,
                               parsed_challenge_option);
@@ -304,11 +320,11 @@
       challenge_option.Set(
           "challenge_id",
           request_details_.selected_challenge_option->id.value());
-      challenge_option.Set(
-          "redirect_url",
-          request_details_.selected_challenge_option->url_to_open.spec());
-      challenge_option.Set("redirect_completion_proof",
-                           request_details_.redirect_completion_proof.value());
+      challenge_option.Set("popup_url",
+                           request_details_.selected_challenge_option
+                               ->vcn_3ds_metadata->url_to_open.spec());
+      challenge_option.Set("redirect_completion_result",
+                           request_details_.redirect_completion_result.value());
       request_dict.Set("redirect_challenge_option",
                        std::move(challenge_option));
     }
diff --git a/components/autofill/core/browser/payments/payments_requests/unmask_card_request_unittest.cc b/components/autofill/core/browser/payments/payments_requests/unmask_card_request_unittest.cc
index 0b86c6c..7323db2 100644
--- a/components/autofill/core/browser/payments/payments_requests/unmask_card_request_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_requests/unmask_card_request_unittest.cc
@@ -302,9 +302,11 @@
       "\"a******b@google.com\"}}, {\"email_otp_challenge_option\": "
       "{\"challenge_id\": \"fake_challenge_id_6\", \"masked_email_address\": "
       "\"c******d@google.com\", \"otp_length\": 4}}, "
-      "{\"redirect_challenge_option\": "
-      "{\"challenge_id\": \"fake_challenge_id_7\", \"redirect_url\": "
-      "\"https://example.com/\"}}]}");
+      "{\"popup_challenge_option\": "
+      "{\"challenge_id\": \"fake_challenge_id_7\", \"popup_url\": "
+      "\"https://example.com/\", \"query_params_for_popup_close\": "
+      "{\"success_query_param_name\": \"token\", "
+      "\"failure_query_param_name\": \"failure\"}}}]}");
   ASSERT_TRUE(response.has_value());
   GetRequest()->ParseResponse(response->GetDict());
 
@@ -375,7 +377,12 @@
     EXPECT_EQ(CardUnmaskChallengeOptionType::kThreeDomainSecure,
               challenge_option_7.type);
     EXPECT_EQ("fake_challenge_id_7", challenge_option_7.id.value());
-    EXPECT_EQ(GURL("https://example.com/"), challenge_option_7.url_to_open);
+    EXPECT_EQ(GURL("https://example.com/"),
+              challenge_option_7.vcn_3ds_metadata->url_to_open);
+    EXPECT_EQ("token",
+              challenge_option_7.vcn_3ds_metadata->success_query_param_name);
+    EXPECT_EQ("failure",
+              challenge_option_7.vcn_3ds_metadata->failure_query_param_name);
     EXPECT_EQ(
         l10n_util::GetStringUTF16(
             IDS_AUTOFILL_CARD_UNMASK_AUTHENTICATION_SELECTION_DIALOG_THREE_DOMAIN_SECURE_CHALLENGE_INFO),
diff --git a/components/autofill/core/browser/payments/payments_window_manager.h b/components/autofill/core/browser/payments/payments_window_manager.h
index 6617905..55229b6 100644
--- a/components/autofill/core/browser/payments/payments_window_manager.h
+++ b/components/autofill/core/browser/payments/payments_window_manager.h
@@ -20,8 +20,8 @@
 // systems.
 class PaymentsWindowManager {
  public:
-  using RedirectCompletionProof =
-      base::StrongAlias<class RedirectCompletionProofTag, std::string>;
+  using RedirectCompletionResult =
+      base::StrongAlias<class RedirectCompletionResultTag, std::string>;
 
   // The response fields for a VCN 3DS authentication, created once a response
   // to the second UnmaskCardRequest has been received.
diff --git a/components/autofill/core/browser/payments/payments_window_manager_util.cc b/components/autofill/core/browser/payments/payments_window_manager_util.cc
index 4e4f06c..a720592 100644
--- a/components/autofill/core/browser/payments/payments_window_manager_util.cc
+++ b/components/autofill/core/browser/payments/payments_window_manager_util.cc
@@ -8,7 +8,6 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_client.h"
-#include "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
 #include "components/autofill/core/browser/payments/payments_util.h"
 #include "components/autofill/core/browser/payments/payments_window_manager.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
@@ -17,11 +16,12 @@
 
 namespace autofill::payments {
 
-base::expected<PaymentsWindowManager::RedirectCompletionProof,
+base::expected<PaymentsWindowManager::RedirectCompletionResult,
                PaymentsWindowManager::Vcn3dsAuthenticationPopupNonSuccessResult>
-ParseUrlForVcn3ds(const GURL& url) {
-  std::optional<bool> should_proceed;
-  std::string redirect_completion_proof;
+ParseUrlForVcn3ds(const GURL& url,
+                  const Vcn3dsChallengeOptionMetadata& metadata) {
+  std::string redirect_completion_result;
+  bool is_failure = false;
   std::string_view query_piece = url.query_piece();
   url::Component query(0, query_piece.length());
   url::Component key;
@@ -29,25 +29,23 @@
   while (url::ExtractQueryKeyValue(query_piece, &query, &key, &value)) {
     std::string_view key_view = query_piece.substr(key.begin, key.len);
     std::string_view value_view = query_piece.substr(value.begin, value.len);
-    if (key_view == "shouldProceed") {
-      should_proceed = value_view == "true";
-    } else if (key_view == "token") {
-      redirect_completion_proof = std::string(value_view);
+    if (key_view == metadata.success_query_param_name) {
+      redirect_completion_result = std::string(value_view);
+    } else if (key_view == metadata.failure_query_param_name) {
+      is_failure = true;
     }
   }
 
-  // `should_proceed` being present, having a value of true, and there being a
-  // `redirect_completion_proof` present indicates the user completed the
+  // `redirect_completion_result` being present indicates the user completed the
   // authentication and a request to the Payments servers is required to
   // retrieve the authentication result.
-  if (should_proceed.value_or(false) && !redirect_completion_proof.empty()) {
-    return base::ok(PaymentsWindowManager::RedirectCompletionProof(
-        redirect_completion_proof));
+  if (!redirect_completion_result.empty()) {
+    return base::ok(PaymentsWindowManager::RedirectCompletionResult(
+        redirect_completion_result));
   }
 
-  // `should_proceed` being present and having a value of false is the Google
-  // Payments server's way of telling Chrome that the authentication failed.
-  if (!should_proceed.value_or(true)) {
+  // `is_failure` being true indicates the authentication has failed.
+  if (is_failure) {
     return base::unexpected(
         PaymentsWindowManager::Vcn3dsAuthenticationPopupNonSuccessResult::
             kAuthenticationFailed);
@@ -62,7 +60,8 @@
 CreateUnmaskRequestDetailsForVcn3ds(
     AutofillClient& client,
     const PaymentsWindowManager::Vcn3dsContext& context,
-    PaymentsWindowManager::RedirectCompletionProof redirect_completion_proof) {
+    PaymentsWindowManager::RedirectCompletionResult
+        redirect_completion_result) {
   payments::PaymentsNetworkInterface::UnmaskRequestDetails request_details;
   request_details.card = context.card;
   request_details.billing_customer_number = GetBillingCustomerId(
@@ -81,8 +80,8 @@
   }
 
   request_details.selected_challenge_option = context.challenge_option;
-  request_details.redirect_completion_proof =
-      std::move(redirect_completion_proof);
+  request_details.redirect_completion_result =
+      std::move(redirect_completion_result);
   return request_details;
 }
 
diff --git a/components/autofill/core/browser/payments/payments_window_manager_util.h b/components/autofill/core/browser/payments/payments_window_manager_util.h
index ee6c6ef..bf1c476 100644
--- a/components/autofill/core/browser/payments/payments_window_manager_util.h
+++ b/components/autofill/core/browser/payments/payments_window_manager_util.h
@@ -8,6 +8,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/types/expected.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
 #include "components/autofill/core/browser/payments/payments_network_interface.h"
 #include "components/autofill/core/browser/payments/payments_window_manager.h"
 
@@ -17,25 +18,27 @@
 
 namespace payments {
 
-// Parses the URL for VCN 3DS, which is set in `url`. If the parsed URL denotes
-// the authentication completed successfully, this function will return a
-// PaymentsWindowManager::RedirectCompletionProof as the expected response.
-// Otherwise this function will return the non-success result.
-base::expected<PaymentsWindowManager::RedirectCompletionProof,
+// Parses the URL for VCN 3DS, which is set in `url`. `metadata` contains the
+// required query parameter information to search for in `url`.  If the parsed
+// URL denotes the authentication completed successfully, this function will
+// return a PaymentsWindowManager::RedirectCompletionResult as the expected
+// response. Otherwise this function will return the non-success result.
+base::expected<PaymentsWindowManager::RedirectCompletionResult,
                PaymentsWindowManager::Vcn3dsAuthenticationPopupNonSuccessResult>
-ParseUrlForVcn3ds(const GURL& url);
+ParseUrlForVcn3ds(const GURL& url,
+                  const Vcn3dsChallengeOptionMetadata& metadata);
 
 // Creates UnmaskRequestDetails specific to VCN 3DS. `client` is the
 // AutofillClient associated with the original browser window. `context` is the
 // context that was set when the flow was initialized, and
-// `redirect_completion_proof` is the token that was parsed from the query
+// `redirect_completion_result` is the token that was parsed from the query
 // parameters in the final redirect of the pop-up. Refer to
-// ParseFinalUrlForVcn3ds() for when `redirect_completion_proof` is set.
+// ParseFinalUrlForVcn3ds() for when `redirect_completion_result` is set.
 PaymentsNetworkInterface::UnmaskRequestDetails
 CreateUnmaskRequestDetailsForVcn3ds(
     AutofillClient& client,
     const PaymentsWindowManager::Vcn3dsContext& context,
-    PaymentsWindowManager::RedirectCompletionProof redirect_completion_proof);
+    PaymentsWindowManager::RedirectCompletionResult redirect_completion_result);
 
 // Creates the Vcn3dsAuthenticationResponse for the response from the
 // UnmaskCardRequest that was sent during the VCN 3DS authentication.
diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h
index baf5283..6b98bd8 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/components/autofill/ios/browser/autofill_driver_ios.h
@@ -152,6 +152,9 @@
   void FormSubmitted(const FormData& form,
                      bool known_success,
                      mojom::SubmissionSource submission_source);
+  void CaretMovedInFormField(const FormData& form,
+                             const FormFieldData& field,
+                             const gfx::Rect& caret_bounds);
   void TextFieldDidChange(const FormData& form,
                           const FormFieldData& field,
                           base::TimeTicks timestamp);
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm
index 96cc5d0..6eb62eb 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -304,6 +304,12 @@
   ClearLastInteractedForm();
 }
 
+void AutofillDriverIOS::CaretMovedInFormField(const FormData& form,
+                                              const FormFieldData& field,
+                                              const gfx::Rect& caret_bounds) {
+  GetAutofillManager().OnCaretMovedInFormField(form, field, caret_bounds);
+}
+
 void AutofillDriverIOS::TextFieldDidChange(const FormData& form,
                                            const FormFieldData& field,
                                            base::TimeTicks timestamp) {
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 94c4fff3..1dab0cbb 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "34.23",
-  "log_list_timestamp": "2024-05-18T13:01:10Z",
+  "version": "34.24",
+  "log_list_timestamp": "2024-05-19T12:56:08Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/compose/core/browser/compose_metrics.h b/components/compose/core/browser/compose_metrics.h
index c8b2761b..bfcc19e 100644
--- a/components/compose/core/browser/compose_metrics.h
+++ b/components/compose/core/browser/compose_metrics.h
@@ -208,26 +208,6 @@
   kMaxValue = kRequestError,
 };
 
-// The output metric for the proactive nudge segmentation model. Represents what
-// effect the nudge had on the user's engagement. Stored as
-// "Compose.ProactiveNudge.DerivedEngagement".
-enum class ProactiveNudgeDerivedEngagement {
-  // The user didn't interact with the nudge.
-  kIgnored,
-  // The user disabled the nudge on this site using the three-dot menu.
-  kNudgeDisabledOnSingleSite,
-  // The user disabled the nudge on all sites using the three-dot menu.
-  kNudgeDisabledOnAllSites,
-  // User clicked the nudge, but didn't press generate in Compose.
-  kOpenedComposeMinimalUse,
-  // User clicked the nudge, pressed generate at least once, but didn't accept
-  // the suggestion.
-  kGeneratedComposeSuggestion,
-  // User clicked, pressed generate, and accepted a suggestion.
-  kAcceptedComposeSuggestion,
-  kMaxValue = kAcceptedComposeSuggestion,
-};
-
 // Struct containing event and logging information for an individual
 // |ComposeSession|.
 struct ComposeSessionEvents {
diff --git a/components/onc/onc_constants.cc b/components/onc/onc_constants.cc
index 75f0a614..e7a4675 100644
--- a/components/onc/onc_constants.cc
+++ b/components/onc/onc_constants.cc
@@ -578,6 +578,9 @@
 const char kRecommendedValuesAreEphemeral[] = "RecommendedValuesAreEphemeral";
 const char kUserCreatedNetworkConfigurationsAreEphemeral[] =
     "UserCreatedNetworkConfigurationsAreEphemeral";
+const char kDisconnectWiFiOnEthernet[] = "DisconnectWiFiOnEthernet";
+const char kDisconnectWiFiOnEthernetWhenConnected[] = "WhenConnected";
+const char kDisconnectWiFiOnEthernetWhenOnline[] = "WhenOnline";
 }  // global_network_config
 
 namespace device_state {
diff --git a/components/onc/onc_constants.h b/components/onc/onc_constants.h
index c18f555..3ce06f27 100644
--- a/components/onc/onc_constants.h
+++ b/components/onc/onc_constants.h
@@ -574,6 +574,10 @@
 COMPONENT_EXPORT(ONC) extern const char kPSIMAdminAssignedAPNs[];
 COMPONENT_EXPORT(ONC)
 extern const char kUserCreatedNetworkConfigurationsAreEphemeral[];
+COMPONENT_EXPORT(ONC) extern const char kDisconnectWiFiOnEthernet[];
+COMPONENT_EXPORT(ONC)
+extern const char kDisconnectWiFiOnEthernetWhenConnected[];
+COMPONENT_EXPORT(ONC) extern const char kDisconnectWiFiOnEthernetWhenOnline[];
 }  // namespace global_network_config
 
 namespace device_state {
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc
index 56276cbc..a96618c3 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -512,29 +512,41 @@
         "form.fields.size()!");
   }
 
-#if BUILDFLAG(IS_ANDROID)
-  if (base::FeatureList::IsEnabled(
-          features::kPasswordSuggestionBottomSheetV2)) {
-    // TODO(crbug.com/40269373): Remove the parameter
-    // autofill::mojom::SubmissionReadinessState::kNoInformation when the
-    // feature is launched.
-    if (client_->ShowKeyboardReplacingSurface(
-            this,
-            PasswordFillingParams(
-                request.form_data, request.username_field_index,
-                request.password_field_index, request.element_id,
-                autofill::mojom::SubmissionReadinessState::kNoInformation),
-            request.show_webauthn_credentials)) {
-      return;
-    }
-  }
-#endif  // BUILDFLAG(IS_ANDROID)
-
-  GetPasswordAutofillManager()->OnShowPasswordSuggestions(
-      request.element_id, request.trigger_source, request.text_direction,
-      request.typed_username,
+  base::OnceClosure show_with_autofill_manager_cb = base::BindOnce(
+      &PasswordAutofillManager::OnShowPasswordSuggestions,
+      GetPasswordAutofillManager()->GetWeakPtr(), request.element_id,
+      request.trigger_source, request.text_direction, request.typed_username,
       ShowWebAuthnCredentials(request.show_webauthn_credentials),
       TransformToRootCoordinates(render_frame_host_, request.bounds));
+#if !BUILDFLAG(IS_ANDROID)
+  std::move(show_with_autofill_manager_cb).Run();
+#else
+  if (!base::FeatureList::IsEnabled(
+          features::kPasswordSuggestionBottomSheetV2)) {
+    std::move(show_with_autofill_manager_cb).Run();
+    return;
+  }
+  // TODO(crbug.com/40269373): Remove the parameter
+  // autofill::mojom::SubmissionReadinessState::kNoInformation when the
+  // feature is launched.
+  client_->ShowKeyboardReplacingSurface(
+      this,
+      PasswordFillingParams(
+          request.form_data, request.username_field_index,
+          request.password_field_index, request.element_id,
+          autofill::mojom::SubmissionReadinessState::kNoInformation),
+      request.show_webauthn_credentials,
+      base::BindOnce(
+          [](base::OnceClosure cb, bool shown) {
+            if (shown) {
+              // UI shown by `client_`, all done.
+              return;
+            }
+            // Otherwise, show with PasswordAutofillManager.
+            std::move(cb).Run();
+          },
+          std::move(show_with_autofill_manager_cb)));
+#endif  // !BUILDFLAG(IS_ANDROID)
 }
 
 #if BUILDFLAG(IS_ANDROID)
@@ -555,7 +567,7 @@
       this,
       PasswordFillingParams(form, 0, 0, autofill::FieldRendererId(),
                             submission_readiness),
-      is_webauthn_form);
+      is_webauthn_form, base::DoNothing());
 }
 #endif
 
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index 29c245c..31340c9 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -477,6 +477,10 @@
   manual_fallback_flow_.swap(manual_fallback_flow);
 }
 
+base::WeakPtr<PasswordAutofillManager> PasswordAutofillManager::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // PasswordAutofillManager, private:
 
diff --git a/components/password_manager/core/browser/password_autofill_manager.h b/components/password_manager/core/browser/password_autofill_manager.h
index acc6b192b..20e1985a 100644
--- a/components/password_manager/core/browser/password_autofill_manager.h
+++ b/components/password_manager/core/browser/password_autofill_manager.h
@@ -116,6 +116,8 @@
     return manual_fallback_flow_.get();
   }
 
+  base::WeakPtr<PasswordAutofillManager> GetWeakPtr();
+
  private:
   // Called just before showing a popup to log which |suggestions| were shown.
   void LogMetricsForSuggestions(
diff --git a/components/password_manager/core/browser/password_manager_client.cc b/components/password_manager/core/browser/password_manager_client.cc
index 5ff8484..f970e5f8 100644
--- a/components/password_manager/core/browser/password_manager_client.cc
+++ b/components/password_manager/core/browser/password_manager_client.cc
@@ -34,11 +34,12 @@
     ErrorMessageFlowType flow_type,
     password_manager::PasswordStoreBackendErrorType error_type) {}
 
-bool PasswordManagerClient::ShowKeyboardReplacingSurface(
+void PasswordManagerClient::ShowKeyboardReplacingSurface(
     PasswordManagerDriver* driver,
     const PasswordFillingParams& password_filling_params,
-    bool is_webauthn_form) {
-  return false;
+    bool is_webauthn_form,
+    base::OnceCallback<void(bool)> shown_cb) {
+  std::move(shown_cb).Run(false);
 }
 #endif
 
diff --git a/components/password_manager/core/browser/password_manager_client.h b/components/password_manager/core/browser/password_manager_client.h
index 155dd2db..8d7be09 100644
--- a/components/password_manager/core/browser/password_manager_client.h
+++ b/components/password_manager/core/browser/password_manager_client.h
@@ -212,11 +212,14 @@
       password_manager::PasswordStoreBackendErrorType error_type);
 
   // Instructs the client to show a keyboard replacing surface UI (e.g.
-  // TouchToFill).
-  virtual bool ShowKeyboardReplacingSurface(
+  // TouchToFill). `shown_cb` will be invoked with whether the view was shown.
+  // TODO(crbug.com/341322405): Make this synchronous again once the account
+  // storage notice is gone.
+  virtual void ShowKeyboardReplacingSurface(
       PasswordManagerDriver* driver,
       const PasswordFillingParams& password_filling_params,
-      bool is_webauthn_form);
+      bool is_webauthn_form,
+      base::OnceCallback<void(bool)> shown_cb);
 #endif
 
   virtual bool CanUseBiometricAuthForFilling(
diff --git a/content/browser/file_system_access/file_system_access_bucket_path_watcher.h b/content/browser/file_system_access/file_system_access_bucket_path_watcher.h
index aebd799d..7b6d9e7 100644
--- a/content/browser/file_system_access/file_system_access_bucket_path_watcher.h
+++ b/content/browser/file_system_access/file_system_access_bucket_path_watcher.h
@@ -21,9 +21,6 @@
 // Watches changes to all bucket file system paths and reports changes to its
 // observers. This class must be created, used, and destroyed on the same
 // sequence as the `FileSystemContext` it holds a reference to.
-//
-// TODO(crbug.com/40105284): Account for changes made by
-// SyncAccessHandles.
 class FileSystemAccessBucketPathWatcher : public FileSystemAccessChangeSource,
                                           public storage::FileChangeObserver {
  public:
diff --git a/content/browser/file_system_access/file_system_access_change_source.h b/content/browser/file_system_access/file_system_access_change_source.h
index d759982c..b0d90a88 100644
--- a/content/browser/file_system_access/file_system_access_change_source.h
+++ b/content/browser/file_system_access/file_system_access_change_source.h
@@ -63,7 +63,7 @@
   // `scope_`. This may fail if the scope cannot be watched.
   // `on_source_initialized` is run with a error status indicating whether
   // setting up this source succeeds.
-  // TODO(crbug.com/40105284): Assert that this is called before
+  // TODO(crbug.com/341095544): Assert that this is called before
   // notifying of changes.
   void EnsureInitialized(
       base::OnceCallback<void(blink::mojom::FileSystemAccessErrorPtr)>
diff --git a/content/browser/file_system_access/file_system_access_observer_browsertest.cc b/content/browser/file_system_access/file_system_access_observer_browsertest.cc
index 7125ca7..c5d6ab2 100644
--- a/content/browser/file_system_access/file_system_access_observer_browsertest.cc
+++ b/content/browser/file_system_access/file_system_access_observer_browsertest.cc
@@ -110,14 +110,14 @@
        return await promise;", \
       base::Int64ToValue(TestTimeouts::action_timeout().InMilliseconds())) +
 
-// TODO(crbug.com/40105284): Consider making these WPTs, and adding a
+// TODO(crbug.com/341136316): Consider making these WPTs, and adding a
 // lot more of them. For example:
 //   - change types
 //   - observing a handle without permission should fail
 //   - changes should not be reported to swap files
-//     (see https://crbug.com/1488874)
+//     (see https://crbug.com/321980149)
 //   - changes should not be reported if permission to the handle is lost
-//     (see https://crbug.com/1489035)
+//     (see https://crbug.com/321980366)
 //   - moving an observed handle
 
 class FileSystemAccessObserverBrowserTestBase : public ContentBrowserTest {
@@ -389,8 +389,8 @@
       return true;
     }
 
-    // TODO(crbug.com/40260973): Some platforms do not support reporting
-    // the modified path.
+    // TODO(crbug.com/321980270, crbug.com/321980447): Some platforms do not
+    // support reporting the modified path.
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     return true;
 #else
@@ -399,8 +399,8 @@
   }
 
   bool SupportsChangeInfo() const {
-    // TODO(crbug.com/40260973): Reporting change info and the modified
-    // path are both only supported on inotify, for now.
+    // TODO(crbug.com/321980270, crbug.com/321980447): Reporting change info and
+    // the modified path are both only supported on inotify, for now.
     return SupportsReportingModifiedPath();
   }
 };
@@ -485,7 +485,6 @@
   EXPECT_THAT(records.GetList(), testing::Not(testing::IsEmpty()));
 }
 
-/// TODO(crbug.com/40939929): Re-enable after fixing flakiness.
 IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                        ObserveDirectoryRecursively) {
   base::FilePath dir_path = CreateDirectoryToBePicked();
@@ -605,7 +604,7 @@
   EXPECT_THAT(records.GetList(), testing::IsEmpty());
 }
 
-// TODO(crbug.com/40283884): Add a ReObserveAfterUnobserve test once the
+// TODO(crbug.com/321980469): Add a ReObserveAfterUnobserve test once the
 // unobserve() method is no longer racy. See https://crrev.com/c/4814709.
 IN_PROC_BROWSER_TEST_P(FileSystemAccessObserverBrowserTest,
                        ReObserveAfterDisconnect) {
@@ -642,10 +641,10 @@
   // clang-format on
   auto records = EvalJs(shell(), script).ExtractList();
   ASSERT_THAT(records.GetList(), testing::Not(testing::IsEmpty()));
-  // TODO(crbug.com/40260973): Support change types for the local file
-  // system on more platforms.
+  // TODO(crbug.com/321980270, crbug.com/321980447): Support change types for
+  // the local file system on more platforms.
   //
-  // TODO(crbug.com/40105284): Consider reporting a consistent change
+  // TODO(crbug.com/340584120): Consider reporting a consistent change
   // type when writing to a file via a WritableFileStream. On the local file
   // system, changes are naively considered "moved" events because the swap file
   // is moved over the target file. Meanwhile, the BucketFS intentionally
@@ -708,9 +707,9 @@
                        ObserveDirectoryReportsCorrectHandle) {
   base::FilePath dir_path = CreateDirectoryToBePicked();
 
-  // TODO(crbug.com/40260973): Some platforms do not report the modified
-  // path. In these cases, `changedHandle` will always be the handle passed to
-  // observe().
+  // TODO(crbug.com/321980270, crbug.com/321980447): Some platforms do not
+  // report the modified path. In these cases, `changedHandle` will always be
+  // the handle passed to observe().
   const std::string changed_handle =
       SupportsReportingModifiedPath() ? "subDir" : "dir";
 
@@ -743,9 +742,9 @@
   // The modified handle is a file, so the change record should contain a
   // FileSystemFileHandle.
   //
-  // TODO(crbug.com/40260973): Some platforms do not report the modified
-  // path. In these cases, `changedHandle` will always be the handle passed to
-  // observe().
+  // TODO(crbug.com/321980270, crbug.com/321980447): Some platforms do not
+  // report the modified path. In these cases, `changedHandle` will always be
+  // the handle passed to observe().
   const std::string changed_handle =
       SupportsReportingModifiedPath() ? "fileInDir" : "dir";
 
diff --git a/content/browser/file_system_access/file_system_access_observer_host.cc b/content/browser/file_system_access/file_system_access_observer_host.cc
index bb91851..66783dd 100644
--- a/content/browser/file_system_access/file_system_access_observer_host.cc
+++ b/content/browser/file_system_access/file_system_access_observer_host.cc
@@ -130,7 +130,7 @@
     return;
   }
 
-  // TODO(crbug.com/40283894): Better handle overlapping observations.
+  // TODO(crbug.com/321980367): Better handle overlapping observations.
   base::EraseIf(observations_, [&](const auto& observation) {
     return observation->handle_url() == resolved_token->url();
   });
diff --git a/content/browser/file_system_access/file_system_access_observer_observation.cc b/content/browser/file_system_access/file_system_access_observer_observation.cc
index 6c3753f..8c548b0 100644
--- a/content/browser/file_system_access/file_system_access_observer_observation.cc
+++ b/content/browser/file_system_access/file_system_access_observer_observation.cc
@@ -166,10 +166,10 @@
   const storage::FileSystemURL& handle_url = AsHandleBase(handle_).url();
 
   // Do not relay changes if the site has lost read permission to the handle.
-  // TODO(crbug.com/40283887): Add tests for this.
+  // TODO(crbug.com/321980366): Add tests for this.
   if (handle_state.read_grant->GetStatus() !=
       blink::mojom::PermissionStatus::GRANTED) {
-    // TODO(crbug.com/40283887): Proactively listen for permission
+    // TODO(crbug.com/321980366): Proactively listen for permission
     // changes, rather than (or perhaps in addition to) checking on each change.
     return;
   }
@@ -177,9 +177,8 @@
   std::vector<blink::mojom::FileSystemAccessChangePtr> mojo_changes;
   for (const auto& change : changes) {
     if (change.type->is_errored()) {
-      // TODO(crbug.com/40105284): Consider destroying `observation_`...
-      // Or don't bother passing along errored changes from the WatcherManager
-      // to its Observations in the first place.
+      // TODO(crbug.com/341123799): Invoke the callback with error type, and
+      // mark this observation as errored so that no further events are sent.
       continue;
     }
 
diff --git a/content/browser/file_system_access/file_system_access_observer_observation.h b/content/browser/file_system_access/file_system_access_observer_observation.h
index 9e1c88bb..697d392 100644
--- a/content/browser/file_system_access/file_system_access_observer_observation.h
+++ b/content/browser/file_system_access/file_system_access_observer_observation.h
@@ -26,7 +26,7 @@
 // call from JavaScript. Forwards changes to the observed file or directory
 // to a mojo pipe whose receiver is owned by the renderer.
 //
-// TODO(crbug.com/40105284): Consider removing this class in favor of
+// TODO(crbug.com/341213353): Consider removing this class in favor of
 // giving the ObserverHost a FileSystemAccessObserver mojo::RemoteSet. See
 // https://chromium-review.googlesource.com/c/chromium/src/+/4809069/comment/8d90508d_74ae7891/.
 class FileSystemAccessObserverObservation : public WebContentsObserver {
diff --git a/content/browser/file_system_access/file_system_access_watch_scope.h b/content/browser/file_system_access/file_system_access_watch_scope.h
index a2bd3450..bdc7325f 100644
--- a/content/browser/file_system_access/file_system_access_watch_scope.h
+++ b/content/browser/file_system_access/file_system_access_watch_scope.h
@@ -16,7 +16,7 @@
 // subdirectories.
 class CONTENT_EXPORT FileSystemAccessWatchScope {
  public:
-  // TODO(crbug.com/40105284): Consider using something like a PassKey
+  // TODO(crbug.com/341239594): Consider using something like a PassKey
   // to restrict access to these initializers.
   static FileSystemAccessWatchScope GetScopeForFileWatch(
       const storage::FileSystemURL& file_url);
diff --git a/content/browser/file_system_access/file_system_access_watch_scope_unittest.cc b/content/browser/file_system_access/file_system_access_watch_scope_unittest.cc
index 5c449270..9e18113 100644
--- a/content/browser/file_system_access/file_system_access_watch_scope_unittest.cc
+++ b/content/browser/file_system_access/file_system_access_watch_scope_unittest.cc
@@ -101,7 +101,7 @@
   EXPECT_FALSE(scope.Contains(child_scope));
   EXPECT_FALSE(child_scope.Contains(scope));
 
-  // TODO(crbug.com/40283896): Test that URLs from different file systems
+  // TODO(crbug.com/321980129): Test that URLs from different file systems
   // return are out of scope.
 }
 
@@ -134,7 +134,7 @@
       FileSystemAccessWatchScope::GetScopeForDirectoryWatch(
           parent_url, /*is_recursive=*/false);
   EXPECT_FALSE(scope.Contains(parent_non_recursive_scope));
-  // TODO(crbug.com/40283894): This is unfortunate. See what can be done
+  // TODO(crbug.com/321980367): This is unfortunate. See what can be done
   // here.
   EXPECT_FALSE(parent_non_recursive_scope.Contains(scope));
 
@@ -164,7 +164,7 @@
   EXPECT_FALSE(scope.Contains(grandchild_scope));
   EXPECT_FALSE(grandchild_scope.Contains(scope));
 
-  // TODO(crbug.com/40283896): Test that URLs from different file systems
+  // TODO(crbug.com/321980129): Test that URLs from different file systems
   // return are out of scope.
 }
 
@@ -225,7 +225,7 @@
   EXPECT_TRUE(scope.Contains(grandchild_scope));
   EXPECT_FALSE(grandchild_scope.Contains(scope));
 
-  // TODO(crbug.com/40283896): Test that URLs from different file systems
+  // TODO(crbug.com/321980129): Test that URLs from different file systems
   // return are out of scope.
 }
 
diff --git a/content/browser/file_system_access/file_system_access_watcher_manager.h b/content/browser/file_system_access/file_system_access_watcher_manager.h
index f977cbe..de74ce3b 100644
--- a/content/browser/file_system_access/file_system_access_watcher_manager.h
+++ b/content/browser/file_system_access/file_system_access_watcher_manager.h
@@ -201,7 +201,7 @@
                  base::UniquePtrComparator>
       observer_hosts_;
 
-  // TODO(crbug.com/40283894): Make more efficient mappings to observers
+  // TODO(crbug.com/321980367): Make more efficient mappings to observers
   // and sources. For now, most actions requires iterating through lists.
 
   // Observations to which this instance will notify of changes within their
diff --git a/content/browser/file_system_access/file_system_access_watcher_manager_unittest.cc b/content/browser/file_system_access/file_system_access_watcher_manager_unittest.cc
index edd018c6..8ce58aa 100644
--- a/content/browser/file_system_access/file_system_access_watcher_manager_unittest.cc
+++ b/content/browser/file_system_access/file_system_access_watcher_manager_unittest.cc
@@ -62,7 +62,8 @@
   loop.Run();
 }
 
-// TODO(crbug.com/40260973): Report the modified path on more platforms.
+// TODO(crbug.com/321980270, crbug.com/321980447): Report the modified path on
+// more platforms.
 bool ReportsModifiedPathForLocalObservations() {
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   return true;
@@ -71,7 +72,8 @@
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 }
 
-// TODO(crbug.com/40260973): Report change info on more platforms.
+// TODO(crbug.com/321980270, crbug.com/321980447): Report change info on more
+// platforms.
 bool ReportsChangeInfoForLocalObservations() {
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   return true;
@@ -428,7 +430,7 @@
   EXPECT_EQ(get_observation_future.Get().error()->status,
             blink::mojom::FileSystemAccessStatus::kOperationFailed);
 
-  // TODO(crbug.com/40105284): Determine what should happen on failure to
+  // TODO(crbug.com/341095544): Determine what should happen on failure to
   // initialize a source, then add better test coverage.
 }
 
@@ -512,7 +514,7 @@
 }
 
 TEST_F(FileSystemAccessWatcherManagerTest, UnsupportedScope) {
-  // TODO(crbug.com/40283896): External backends are not yet supported.
+  // TODO(crbug.com/321980129): External backends are not yet supported.
   base::FilePath test_external_path =
       base::FilePath::FromUTF8Unsafe(kTestMountPoint).AppendASCII("foo");
   auto external_url = manager_->CreateFileSystemURLFromPath(
@@ -529,7 +531,7 @@
             blink::mojom::FileSystemAccessStatus::kNotSupportedError);
 }
 
-// TODO(crbug.com/40283894): Add tests covering more edge cases regarding
+// TODO(crbug.com/321980367): Add tests covering more edge cases regarding
 // overlapping scopes.
 TEST_F(FileSystemAccessWatcherManagerTest, OverlappingSourceScopes) {
   base::FilePath dir_path = dir_.GetPath().AppendASCII("dir");
@@ -567,7 +569,7 @@
   source_for_file.Signal();
   source_for_dir.Signal(/*relative_path=*/file_path.BaseName());
 
-  // TODO(crbug.com/40268906): It would be nice if the watcher manager
+  // TODO(crbug.com/321980367): It would be nice if the watcher manager
   // could consolidate these changes....
 
   Change expected_change{
@@ -784,7 +786,7 @@
   }));
 }
 
-// TODO(crbug.com/40105284): Consider parameterizing these tests once
+// TODO(crbug.com/321980129): Consider parameterizing these tests once
 // observing changes to other backends is supported.
 
 TEST_F(FileSystemAccessWatcherManagerTest, WatchLocalDirectory) {
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 4771952..26784f5c 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -979,6 +979,12 @@
     head->service_worker_router_info =
         std::move(head_update_params_.router_info);
   }
+  if (!head_update_params_.load_timing_info
+           .service_worker_router_evaluation_start.is_null()) {
+    head->load_timing.service_worker_router_evaluation_start =
+        head_update_params_.load_timing_info
+            .service_worker_router_evaluation_start;
+  }
 
   // If the default loader (network) was used to handle the URL load request
   // we need to see if the interceptors want to potentially create a new
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index bab7619..dcb6634 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -5282,6 +5282,49 @@
 
 IN_PROC_BROWSER_TEST_F(
     ServiceWorkerStaticRouterRaceNetworkAndFetchHandlerSourceBrowserTest,
+    NetworkRequest_Wins_PassThrough) {
+  // Register the ServiceWorker and navigate to the in scope URL.
+  SetupAndRegisterServiceWorker();
+  // Capture the response head.
+  const GURL test_url = embedded_test_server()->GetURL(
+      "/service_worker/mock_response?sw_slow&sw_pass_through");
+
+  NavigationHandleObserver observer(web_contents(), test_url);
+  NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1);
+  EXPECT_TRUE(observer.has_committed());
+
+  // ServiceWorker will respond after the delay, so we expect the response from
+  // the network request initiated by the RaceNetworkRequest mode comes first.
+  EXPECT_EQ("[ServiceWorkerRaceNetworkRequest] Response from the network",
+            GetInnerText());
+
+  // Check the response header. "X-Response-From: fetch-handler" is returned
+  // when the result from the fetch handler is used.
+  EXPECT_NE("fetch-handler",
+            observer.GetNormalizedResponseHeader("X-Response-From"));
+
+  // Dispatch another request that returns a response from the fetch handler,
+  // which should ensure the first fetch handler execution has finished.
+  EXPECT_EQ("[ServiceWorkerRaceNetworkRequest] Response from the fetch handler",
+            EvalJs(GetPrimaryMainFrame(),
+                   "fetch('/service_worker/no_race?sw_respond').then(response "
+                   "=> response.text())"));
+
+  // Check the network error count happened inside the fetch handler.
+  const std::string script = R"(
+    new Promise((resolve, reject) => {
+      navigator.serviceWorker.addEventListener('message', (event) => {
+        resolve(event.data.length);
+      });
+      navigator.serviceWorker.controller.postMessage('errors');
+    });
+  )";
+
+  EXPECT_EQ(0, EvalJs(GetPrimaryMainFrame(), script));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    ServiceWorkerStaticRouterRaceNetworkAndFetchHandlerSourceBrowserTest,
     NetworkRequest_Wins_MarkedAsSecure) {
   // Register the ServiceWorker and navigate to the in scope URL.
   StartServerAndNavigateToSetup();
@@ -6563,9 +6606,9 @@
 
  private:
   base::test::ScopedFeatureList feature_list_;
-  std::string kValidChecksum =
-      "042D3C49B5FA366582CEBA01E6E3DCD6259531CCAF64B26635B7F0C11C18BE0D";
-  std::string kInvalidChecksum = "";
+  static constexpr char kValidChecksum[] =
+      "E3F7EBC59086064254D833F18B01BAAE4B9DB5F5321E271AC345F2648518324A";
+  static constexpr char kInvalidChecksum[] = "";
 };
 
 INSTANTIATE_TEST_SUITE_P(ALL,
diff --git a/content/browser/service_worker/service_worker_cache_storage_matcher.cc b/content/browser/service_worker/service_worker_cache_storage_matcher.cc
index a359791d..f7513a1 100644
--- a/content/browser/service_worker/service_worker_cache_storage_matcher.cc
+++ b/content/browser/service_worker/service_worker_cache_storage_matcher.cc
@@ -52,7 +52,8 @@
                          "ServiceWorkerCacheStorageMatcher::Run",
                          TRACE_ID_LOCAL(this),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
-  dispatch_event_time_ = base::TimeTicks::Now();
+  CHECK(cache_lookup_start_.is_null());
+  cache_lookup_start_ = base::TimeTicks::Now();
   // If `GetMainScriptResponse` is not set, it need to be set from the
   // installed script.  Or, calling the fallback function may fail.
   if (!version_->GetMainScriptResponse()) {
@@ -116,8 +117,6 @@
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
 
   auto timing = blink::mojom::ServiceWorkerFetchEventTiming::New();
-  timing->dispatch_event_time = dispatch_event_time_;
-  timing->respond_with_settled_time = base::TimeTicks::Now();
   switch (result->which()) {
     case blink::mojom::MatchResult::Tag::kStatus:  // error fallback.
       base::UmaHistogramEnumeration(
@@ -159,9 +158,6 @@
                          "ServiceWorkerCacheStorageMatcher::FailFallback",
                          TRACE_ID_LOCAL(this),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
-  auto timing = blink::mojom::ServiceWorkerFetchEventTiming::New();
-  timing->dispatch_event_time = dispatch_event_time_;
-  timing->respond_with_settled_time = base::TimeTicks::Now();
 
   // `Run` method will be called in
   // `ServiceWorkerMainResourceLoader::StartRequest`.
@@ -178,7 +174,8 @@
           weak_ptr_factory_.GetWeakPtr(),
           blink::ServiceWorkerStatusCode::kErrorFailed,
           ServiceWorkerFetchDispatcher::FetchEventResult::kShouldFallback,
-          blink::mojom::FetchAPIResponse::New(), nullptr, std::move(timing)));
+          blink::mojom::FetchAPIResponse::New(), nullptr,
+          blink::mojom::ServiceWorkerFetchEventTiming::New()));
   return;
 }
 
diff --git a/content/browser/service_worker/service_worker_cache_storage_matcher.h b/content/browser/service_worker/service_worker_cache_storage_matcher.h
index f755d5a3..c0f0f50 100644
--- a/content/browser/service_worker/service_worker_cache_storage_matcher.h
+++ b/content/browser/service_worker/service_worker_cache_storage_matcher.h
@@ -48,6 +48,8 @@
 
   void Run();
 
+  base::TimeTicks cache_lookup_start() { return cache_lookup_start_; }
+
  private:
   void FailFallback();
   void DidMatch(blink::mojom::MatchResultPtr result);
@@ -63,7 +65,7 @@
   ServiceWorkerFetchDispatcher::FetchCallback fetch_callback_;
 
   mojo::Remote<blink::mojom::CacheStorage> remote_;
-  base::TimeTicks dispatch_event_time_;
+  base::TimeTicks cache_lookup_start_;
 
   std::unique_ptr<ServiceWorkerInstalledScriptsSender>
       installed_scripts_sender_;
diff --git a/content/browser/service_worker/service_worker_main_resource_loader.cc b/content/browser/service_worker/service_worker_main_resource_loader.cc
index 6751d29..d15c637 100644
--- a/content/browser/service_worker/service_worker_main_resource_loader.cc
+++ b/content/browser/service_worker/service_worker_main_resource_loader.cc
@@ -270,6 +270,8 @@
         active_worker->router_evaluator()->rules().rules.size();
     router_info->evaluation_worker_status = worker_status;
 
+    response_head_->load_timing.service_worker_router_evaluation_start =
+        base::TimeTicks::Now();
     auto eval_result = active_worker->router_evaluator()->Evaluate(
         resource_request_, running_status);
     // ServiceWorkerStaticRouter_Evaluate is counted only here.
@@ -282,7 +284,7 @@
     if (eval_result) {  // matched the rule.
       const auto& sources = eval_result->sources;
       auto source_type = sources[0].type;
-      set_used_router_source_type(source_type);
+      set_matched_router_source_type(source_type);
       router_info->rule_id_matched = eval_result->id;
       router_info->matched_source_type = source_type;
 
@@ -309,9 +311,11 @@
                   [](NavigationLoaderInterceptor::FallbackCallback
                          fallback_callback,
                      scoped_refptr<ServiceWorkerVersion> active_worker,
-                     network::mojom::ServiceWorkerRouterInfoPtr router_info) {
+                     network::mojom::ServiceWorkerRouterInfoPtr router_info,
+                     net::LoadTimingInfo load_timing_info) {
                     ResponseHeadUpdateParams head_update_params;
                     head_update_params.router_info = std::move(router_info);
+                    head_update_params.load_timing_info = load_timing_info;
                     std::move(fallback_callback)
                         .Run(std::move(head_update_params));
                     if (active_worker->running_status() !=
@@ -325,7 +329,8 @@
                     }
                   },
                   std::move(fallback_callback_), active_worker,
-                  std::move(response_head_->service_worker_router_info)));
+                  std::move(response_head_->service_worker_router_info),
+                  response_head_->load_timing));
           return;
         case network::mojom::ServiceWorkerRouterSourceType::kRace:
           race_network_request_mode = RaceNetworkRequestMode::kForced;
@@ -613,14 +618,21 @@
   TransitionToStatus(Status::kSentBody);
 
   // When a `response_head` is not `response_head_`, set the
-  // `service_worker_router_info` manually to pass the correct routing
-  // information. Currently, this is only applicable to when
-  // `race-network-and-fetch` is specified, and when this method is called
-  // from `ServiceWorkerRaceNetworkRequestURLLoaderClient`.
-  if (response_head_.get() != response_head.get() &&
-      response_head_->service_worker_router_info) {
-    response_head->service_worker_router_info =
-        std::move(response_head_->service_worker_router_info);
+  // `service_worker_router_info` and relevant fields in `load_timing` manually
+  // to pass the correct routing information. Currently, this is only applicable
+  // to when `race-network-and-fetch` is specified, and when this method is
+  // called from `ServiceWorkerRaceNetworkRequestURLLoaderClient`.
+  if (response_head_.get() != response_head.get()) {
+    if (response_head_->service_worker_router_info) {
+      response_head->service_worker_router_info =
+          std::move(response_head_->service_worker_router_info);
+    }
+
+    if (!response_head_->load_timing.service_worker_router_evaluation_start
+             .is_null()) {
+      response_head->load_timing.service_worker_router_evaluation_start =
+          response_head_->load_timing.service_worker_router_evaluation_start;
+    }
   }
 
   url_loader_client_->OnReceiveResponse(response_head.Clone(),
@@ -702,6 +714,16 @@
       blink::ServiceWorkerStatusToString(status), "result",
       ComposeFetchEventResultString(fetch_result, *response));
 
+  // When kRaceNetworkRequest preload is triggered, it's possible that the
+  // response is already committed without waiting for the fetch event result.
+  // Invalidate and destruct if the class already detached from the request.
+  has_fetch_event_finished_ = true;
+  if (dispatched_preload_type() == DispatchedPreloadType::kRaceNetworkRequest &&
+      is_detached_ && status_ == Status::kCompleted) {
+    InvalidateAndDeleteIfNeeded();
+    return;
+  }
+
   bool is_fallback =
       fetch_result ==
       ServiceWorkerFetchDispatcher::FetchEventResult::kShouldFallback;
@@ -847,20 +869,29 @@
     return;
   }
 
+  if (IsMatchedRouterSourceType(
+          network::mojom::ServiceWorkerRouterSourceType::kCache)) {
+    CHECK(cache_matcher_);
+    response_head_->load_timing.service_worker_cache_lookup_start =
+        cache_matcher_->cache_lookup_start();
+  }
+
   // Record the timing of when the fetch event is dispatched on the worker
-  // thread. This is used for PerformanceResourceTiming#fetchStart and
-  // PerformanceResourceTiming#requestStart, but it's still under spec
-  // discussion.
-  // See https://github.com/w3c/resource-timing/issues/119 for more details.
-  // Exposed as PerformanceResourceTiming#fetchStart.
-  response_head_->load_timing.service_worker_ready_time =
-      fetch_event_timing_->dispatch_event_time;
-  // Exposed as PerformanceResourceTiming#requestStart.
-  response_head_->load_timing.send_start =
-      fetch_event_timing_->dispatch_event_time;
-  // Recorded for the DevTools.
-  response_head_->load_timing.send_end =
-      fetch_event_timing_->dispatch_event_time;
+  // thread, when the fetch start for service worker should exist.
+  // This means that the static routing API is not used, or the API is used
+  // with `fetch-event` or `race`. This is used for
+  // PerformanceResourceTiming#fetchStart and
+  // PerformanceResourceTiming#requestStart.
+  if (ShouldRecordServiceWorkerFetchStart()) {
+    response_head_->load_timing.service_worker_ready_time =
+        fetch_event_timing_->dispatch_event_time;
+    // Exposed as PerformanceResourceTiming#requestStart.
+    response_head_->load_timing.send_start =
+        fetch_event_timing_->dispatch_event_time;
+    // Recorded for the DevTools.
+    response_head_->load_timing.send_end =
+        fetch_event_timing_->dispatch_event_time;
+  }
 
   // Records the metrics only if the code has been executed successfully in
   // the service workers because we aim to see the fallback ratio and timing.
@@ -926,7 +957,7 @@
   response_head_->load_timing.receive_headers_end =
       response_head_->load_timing.receive_headers_start;
   response_source_ = response->response_source;
-  if (!ShouldAvoidRecordingServiceWorkerTimingInfo()) {
+  if (ShouldRecordServiceWorkerFetchStart()) {
     response_head_->load_timing.service_worker_fetch_start =
         fetch_event_timing_->dispatch_event_time;
     response_head_->load_timing.service_worker_respond_with_settled =
@@ -1080,6 +1111,20 @@
   TRACE_EVENT_WITH_FLOW0(
       "ServiceWorker", "ServiceWorkerMainResourceLoader::OnConnectionClosed",
       this, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
+  InvalidateAndDeleteIfNeeded();
+}
+
+void ServiceWorkerMainResourceLoader::InvalidateAndDeleteIfNeeded() {
+  // Postpone the invalidation and destruction if both conditions are satisfied:
+  // 1) RaceNetworkRequest is dispatched and the network wins the race.
+  // 2) The fetch event result is not received yet.
+  // The postponed things will be done in DidDispatchFetchEvent().
+  if (dispatched_preload_type() == DispatchedPreloadType::kRaceNetworkRequest &&
+      race_network_request_url_loader_client_.has_value() &&
+      !has_fetch_event_finished_) {
+    CHECK(fetch_dispatcher_);
+    return;
+  }
 
   // The fetch dispatcher or stream waiter may still be running. Don't let them
   // do callbacks back to this loader, since it is now done with the request.
@@ -1239,7 +1284,7 @@
     return false;
   }
 
-  if (ShouldAvoidRecordingServiceWorkerTimingInfo()) {
+  if (!ShouldRecordServiceWorkerFetchStart()) {
     return false;
   }
 
diff --git a/content/browser/service_worker/service_worker_main_resource_loader.h b/content/browser/service_worker/service_worker_main_resource_loader.h
index 110c70b..c110121 100644
--- a/content/browser/service_worker/service_worker_main_resource_loader.h
+++ b/content/browser/service_worker/service_worker_main_resource_loader.h
@@ -183,6 +183,7 @@
   void SetCommitResponsibility(FetchResponseFrom fetch_response_from) override;
 
   void OnConnectionClosed();
+  void InvalidateAndDeleteIfNeeded();
   void DeleteIfNeeded();
 
   network::mojom::ServiceWorkerStatus ConvertToServiceWorkerStatus(
@@ -309,6 +310,8 @@
   // enabled.
   std::string worker_parent_client_uuid_;
 
+  bool has_fetch_event_finished_ = false;
+
   base::WeakPtrFactory<ServiceWorkerMainResourceLoader> weak_factory_{this};
 };
 
diff --git a/content/common/service_worker/service_worker_resource_loader.cc b/content/common/service_worker/service_worker_resource_loader.cc
index 8f5c7ce..51143ad 100644
--- a/content/common/service_worker/service_worker_resource_loader.cc
+++ b/content/common/service_worker/service_worker_resource_loader.cc
@@ -84,22 +84,29 @@
   dispatched_preload_type_ = type;
 }
 
-bool ServiceWorkerResourceLoader::
-    ShouldAvoidRecordingServiceWorkerTimingInfo() {
-  if (!used_router_source_type_.has_value()) {
+bool ServiceWorkerResourceLoader::ShouldRecordServiceWorkerFetchStart() {
+  if (!matched_router_source_type_.has_value()) {
+    return true;
+  }
+
+  switch (*matched_router_source_type_) {
+    case network::mojom::ServiceWorkerRouterSourceType::kNetwork:
+    case network::mojom::ServiceWorkerRouterSourceType::kCache:
+      return false;
+    case network::mojom::ServiceWorkerRouterSourceType::kRace:
+    case network::mojom::ServiceWorkerRouterSourceType::kFetchEvent:
+      // These source should start ServiceWorker and trigger fetch-event.
+      return true;
+  }
+}
+
+bool ServiceWorkerResourceLoader::IsMatchedRouterSourceType(
+    network::mojom::ServiceWorkerRouterSourceType type) {
+  if (!matched_router_source_type_.has_value()) {
     return false;
   }
 
-  switch (*used_router_source_type_) {
-    case network::mojom::ServiceWorkerRouterSourceType::kNetwork:
-    case network::mojom::ServiceWorkerRouterSourceType::kCache:
-      return true;
-    case network::mojom::ServiceWorkerRouterSourceType::kRace:
-    case network::mojom::ServiceWorkerRouterSourceType::kFetchEvent:
-      // It is fine to record the ServiceWorker related metrics
-      // because the fetch handler is executed.
-      return false;
-  }
+  return *matched_router_source_type_ == type;
 }
 
 }  // namespace content
diff --git a/content/common/service_worker/service_worker_resource_loader.h b/content/common/service_worker/service_worker_resource_loader.h
index 6bf46328..40d0622 100644
--- a/content/common/service_worker/service_worker_resource_loader.h
+++ b/content/common/service_worker/service_worker_resource_loader.h
@@ -110,24 +110,24 @@
       const net::RedirectInfo& redirect_info,
       const network::mojom::URLResponseHeadPtr& response_head) = 0;
 
-  // TODO(crbug.com/41496865): remove the function after the spec has been
-  // decided and the implementation is ready.
-  //
-  // Currently, timing info for the ServiceWorker static routing API
-  // has not been decided yet.  To avoid unnecessary confusion, no metrics
-  // are recorded if the fetch handler is not executed. i.e. cache or network
-  // sources are used.
-  bool ShouldAvoidRecordingServiceWorkerTimingInfo();
-  void set_used_router_source_type(
+  // Determine if the fetch start should be recorded, by checking the matched
+  // source type of ServiceWorker static routing API. If no source is matched,
+  // or the source is matched to `race` or `fetch-event`, we should record fetch
+  // start time since these cases will start the ServiceWorker and trigger fetch
+  // event.
+  bool ShouldRecordServiceWorkerFetchStart();
+  bool IsMatchedRouterSourceType(
+      network::mojom::ServiceWorkerRouterSourceType type);
+  void set_matched_router_source_type(
       network::mojom::ServiceWorkerRouterSourceType type) {
-    used_router_source_type_ = type;
+    matched_router_source_type_ = type;
   }
 
  private:
   FetchResponseFrom commit_responsibility_ = FetchResponseFrom::kNoResponseYet;
   DispatchedPreloadType dispatched_preload_type_ = DispatchedPreloadType::kNone;
   std::optional<network::mojom::ServiceWorkerRouterSourceType>
-      used_router_source_type_;
+      matched_router_source_type_;
 };
 }  // namespace content
 
diff --git a/content/renderer/service_worker/service_worker_subresource_loader.cc b/content/renderer/service_worker/service_worker_subresource_loader.cc
index 5bc5b5bd..bee44928 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader.cc
@@ -30,6 +30,7 @@
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/mojom/early_hints.mojom.h"
+#include "services/network/public/mojom/service_worker_router_info.mojom-shared.h"
 #include "services/network/public/mojom/service_worker_router_info.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/common/blob/blob_utils.h"
@@ -39,6 +40,7 @@
 #include "third_party/blink/public/mojom/service_worker/dispatch_fetch_event_params.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_fetch_handler_bypass_option.mojom-shared.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_stream_handle.mojom.h"
+#include "third_party/blink/public/platform/web_url_response.h"
 
 namespace content {
 
@@ -57,6 +59,7 @@
 network::mojom::URLResponseHeadPtr RewriteResponseHead(
     base::TimeTicks service_worker_start_time,
     base::TimeTicks service_worker_ready_time,
+    base::TimeTicks service_worker_router_evaluation_start,
     std::optional<network::mojom::ServiceWorkerRouterInfo>
         service_worker_router_info,
     network::mojom::URLResponseHeadPtr response_head) {
@@ -64,6 +67,8 @@
       service_worker_start_time;
   response_head->load_timing.service_worker_ready_time =
       service_worker_ready_time;
+  response_head->load_timing.service_worker_router_evaluation_start =
+      service_worker_router_evaluation_start;
   if (service_worker_router_info) {
     response_head->service_worker_router_info =
         network::mojom::ServiceWorkerRouterInfo::New(
@@ -377,11 +382,13 @@
         network::mojom::ServiceWorkerRouterInfo::New();
     auto* router_info = response_head_->service_worker_router_info.get();
 
+    response_head_->load_timing.service_worker_router_evaluation_start =
+        base::TimeTicks::Now();
     const auto eval_result = EvaluateRouterConditions();
     if (eval_result) {  // matched the rule.
       const auto& sources = eval_result->sources;
       auto source_type = sources[0].type;
-      set_used_router_source_type(source_type);
+      set_matched_router_source_type(source_type);
 
       router_info->rule_id_matched = eval_result->id;
       router_info->matched_source_type = source_type;
@@ -728,13 +735,17 @@
   std::optional<network::mojom::ServiceWorkerRouterInfo> router_info;
   if (response_head_->service_worker_router_info) {
     router_info = *response_head_->service_worker_router_info;
+    router_info->actual_source_type =
+        network::mojom::ServiceWorkerRouterSourceType::kNetwork;
   }
   auto client_impl = std::make_unique<HeaderRewritingURLLoaderClient>(
       std::move(url_loader_client_),
-      base::BindRepeating(&RewriteResponseHead,
-                          response_head_->load_timing.service_worker_start_time,
-                          response_head_->load_timing.service_worker_ready_time,
-                          router_info));
+      base::BindRepeating(
+          &RewriteResponseHead,
+          response_head_->load_timing.service_worker_start_time,
+          response_head_->load_timing.service_worker_ready_time,
+          response_head_->load_timing.service_worker_router_evaluation_start,
+          router_info));
   mojo::MakeSelfOwnedReceiver(std::move(client_impl),
                               client.InitWithNewPipeAndPassReceiver());
 
@@ -760,7 +771,7 @@
 
 void ServiceWorkerSubresourceLoader::UpdateResponseTiming(
     blink::mojom::ServiceWorkerFetchEventTimingPtr timing) {
-  if (!ShouldAvoidRecordingServiceWorkerTimingInfo()) {
+  if (ShouldRecordServiceWorkerFetchStart()) {
     // |service_worker_ready_time| becomes web-exposed
     // PerformanceResourceTiming#fetchStart, which is the time just before
     // dispatching the fetch event, so set it to |dispatch_event_time|.
@@ -832,6 +843,14 @@
   // client.
   response_head_->timing_allow_passed = true;
 
+  // Set the actual source type to `kFetchEvent` if nothing is set yet.
+  auto* router_info = response_head_->service_worker_router_info.get();
+  if (router_info && router_info->matched_source_type &&
+      !router_info->actual_source_type) {
+    router_info->actual_source_type =
+        network::mojom::ServiceWorkerRouterSourceType::kFetchEvent;
+  }
+
   // Handle a redirect response. ComputeRedirectInfo returns non-null redirect
   // info if the given response is a redirect.
   std::optional<net::RedirectInfo> redirect_info =
@@ -920,6 +939,23 @@
     mojo::ScopedDataPipeConsumerHandle response_body,
     std::optional<mojo_base::BigBuffer> cached_metadata) {
   TransitionToStatus(Status::kSentBody);
+  // When a `response_head` is not `response_head_`, set the
+  // `service_worker_router_info` and relevant fields in `load_timing` manually
+  // to pass the correct routing information. Currently, this is only applicable
+  // to when `race-network-and-fetch` is specified, and when this method is
+  // called from `ServiceWorkerRaceNetworkRequestURLLoaderClient`.
+  if (response_head_.get() != response_head.get()) {
+    if (response_head_->service_worker_router_info) {
+      response_head->service_worker_router_info =
+          std::move(response_head_->service_worker_router_info);
+    }
+
+    if (!response_head_->load_timing.service_worker_router_evaluation_start
+             .is_null()) {
+      response_head->load_timing.service_worker_router_evaluation_start =
+          response_head_->load_timing.service_worker_router_evaluation_start;
+    }
+  }
   // TODO(kinuko): Fill the ssl_info.
   url_loader_client_->OnReceiveResponse(response_head.Clone(),
                                         std::move(response_body),
@@ -1058,7 +1094,7 @@
       "ServiceWorker", "ServiceWorker.LoadTiming.Subresource", this,
       completion_time_);
 
-  if (ShouldAvoidRecordingServiceWorkerTimingInfo()) {
+  if (!ShouldRecordServiceWorkerFetchStart()) {
     return false;
   }
 
@@ -1295,6 +1331,26 @@
   return false;
 }
 
+void ServiceWorkerSubresourceLoader::SetCommitResponsibility(
+    FetchResponseFrom fetch_response_from) {
+  // Set the actual source type used in Static Routing API when
+  // `race-network-and-fetch` is used. Determine this by checking the
+  // commit responsibility. If it's not the service worker, the network
+  // has won.
+  // This check is conducted here since in the case of `knetwork`, it does
+  // not call `DidDispatchFetchEvent`, where we set the `actual_source_type`
+  // for the other sources, and the `response_head_` is already passed on.
+  if (response_head_ && response_head_->service_worker_router_info &&
+      response_head_->service_worker_router_info->matched_source_type &&
+      *response_head_->service_worker_router_info->matched_source_type ==
+          network::mojom::ServiceWorkerRouterSourceType::kRace &&
+      fetch_response_from == FetchResponseFrom::kWithoutServiceWorker) {
+    response_head_->service_worker_router_info->actual_source_type =
+        network::mojom::ServiceWorkerRouterSourceType::kNetwork;
+  }
+  ServiceWorkerResourceLoader::SetCommitResponsibility(fetch_response_from);
+}
+
 std::optional<ServiceWorkerRouterEvaluator::Result>
 ServiceWorkerSubresourceLoader::EvaluateRouterConditions() const {
   auto* router_evaluator = controller_connector_->router_evaluator();
@@ -1416,8 +1472,8 @@
     base::TimeTicks event_dispatch_time,
     blink::mojom::MatchResultPtr result) {
   auto timing = blink::mojom::ServiceWorkerFetchEventTiming::New();
-  timing->dispatch_event_time = event_dispatch_time;
-  timing->respond_with_settled_time = base::TimeTicks::Now();
+  response_head_->load_timing.service_worker_cache_lookup_start =
+      event_dispatch_time;
   switch (result->which()) {
     case blink::mojom::MatchResult::Tag::kStatus:  // error fallback.
       base::UmaHistogramEnumeration(
@@ -1438,6 +1494,9 @@
         // third_party/blink/renderer/modules/cache_storage/cache_storage.cc)
         result->get_response()->parsed_headers.reset();
       }
+      CHECK(response_head_->service_worker_router_info);
+      response_head_->service_worker_router_info->actual_source_type =
+          network::mojom::ServiceWorkerRouterSourceType::kCache;
       OnResponse(std::move(result->get_response()), std::move(timing));
       return;
     case blink::mojom::MatchResult::Tag::kEagerResponse:
diff --git a/content/renderer/service_worker/service_worker_subresource_loader.h b/content/renderer/service_worker/service_worker_subresource_loader.h
index 93defd8..5493e169 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader.h
+++ b/content/renderer/service_worker/service_worker_subresource_loader.h
@@ -143,6 +143,8 @@
                                  std::optional<mojo_base::BigBuffer> metadata);
   void OnBodyReadingComplete(int net_error);
 
+  void SetCommitResponsibility(FetchResponseFrom fetch_response_from) override;
+
   // ServiceWorkerResourceLoader overrides:
   void CommitResponseHeaders(
       const network::mojom::URLResponseHeadPtr&) override;
diff --git a/content/test/data/service_worker/race_network_request_base.js b/content/test/data/service_worker/race_network_request_base.js
index 16b3fac..848bc2c 100644
--- a/content/test/data/service_worker/race_network_request_base.js
+++ b/content/test/data/service_worker/race_network_request_base.js
@@ -16,11 +16,13 @@
       options);
 };
 
+const errors = [];
+
 self.addEventListener('activate', e => {
   e.waitUntil(clients.claim());
 });
 
-self.addEventListener("fetch", e => {
+self.addEventListener('fetch', e => {
   const {request} = e;
   const url = new URL(request.url);
 
@@ -45,10 +47,20 @@
   }
 
   if (url.search.includes('sw_pass_through')) {
-    e.respondWith(fetch(request));
+    e.respondWith(fetch(request).catch(err => {
+      errors.push(err);
+    }));
   }
 
   if (url.search.includes('sw_clone_pass_through')) {
-    e.respondWith(fetch(request.clone()));
+    e.respondWith(fetch(request.clone()).catch(err => {
+      errors.push(err);
+    }));
+  }
+});
+
+self.addEventListener('message', e => {
+  if (e.data == 'errors') {
+    e.source.postMessage(errors);
   }
 });
diff --git a/content/test/data/service_worker/static_router_race_match_all.js b/content/test/data/service_worker/static_router_race_match_all.js
index b061a5f..e241825 100644
--- a/content/test/data/service_worker/static_router_race_match_all.js
+++ b/content/test/data/service_worker/static_router_race_match_all.js
@@ -4,11 +4,15 @@
 importScripts('./race_network_request_base.js');
 
 self.addEventListener('install', e => {
-  e.addRoutes({
-    condition: {
-      urlPattern: new URLPattern({})
+  e.addRoutes([
+    {
+      condition: {urlPattern: {pathname: '/service_worker/no_race'}},
+      source: 'fetch-event'
     },
-    source: "race-network-and-fetch-handler"
-  });
+    {
+      condition: {urlPattern: new URLPattern({})},
+      source: 'race-network-and-fetch-handler'
+    }
+  ]);
   self.skipWaiting();
 });
diff --git a/gpu/config/software_rendering_list.json b/gpu/config/software_rendering_list.json
index 06b4d739..1155070 100644
--- a/gpu/config/software_rendering_list.json
+++ b/gpu/config/software_rendering_list.json
@@ -1498,31 +1498,6 @@
       "features": [
         "skia_graphite"
       ]
-    },
-    {
-      "id": 184,
-      "cr_bugs": [340005431],
-      "description": "Accelerated video decoding fails with SkiaGraphite + NVIDIA GTX 960",
-      "os": {
-        "type": "win"
-      },
-      "vendor_id": "0x10de",
-      "device_id": ["0x1401"],
-      "features": [
-        "skia_graphite"
-      ]
-    },
-    {
-      "id": 185,
-      "cr_bugs": [340005431],
-      "description": "Accelerated video decoding fails with SkiaGraphite + Moore Threads GPUs",
-      "os": {
-        "type": "win"
-      },
-      "vendor_id": "0x1ed5",
-      "features": [
-        "skia_graphite"
-      ]
     }
   ]
 }
diff --git a/infra/config/generated/builders/reclient/Mac Builder reclient staging untrusted/gn-args.json b/infra/config/generated/builders/reclient/Mac Builder reclient staging untrusted/gn-args.json
index 5b66695..4ed67c6 100644
--- a/infra/config/generated/builders/reclient/Mac Builder reclient staging untrusted/gn-args.json
+++ b/infra/config/generated/builders/reclient/Mac Builder reclient staging untrusted/gn-args.json
@@ -6,6 +6,7 @@
     "is_debug": false,
     "proprietary_codecs": true,
     "symbol_level": 1,
+    "target_cpu": "x64",
     "use_remoteexec": true,
     "use_siso": true
   }
diff --git a/infra/config/generated/builders/reclient/Mac Builder reclient staging/gn-args.json b/infra/config/generated/builders/reclient/Mac Builder reclient staging/gn-args.json
index 5b66695..4ed67c6 100644
--- a/infra/config/generated/builders/reclient/Mac Builder reclient staging/gn-args.json
+++ b/infra/config/generated/builders/reclient/Mac Builder reclient staging/gn-args.json
@@ -6,6 +6,7 @@
     "is_debug": false,
     "proprietary_codecs": true,
     "symbol_level": 1,
+    "target_cpu": "x64",
     "use_remoteexec": true,
     "use_siso": true
   }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 26463f9..868c8db 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -62085,8 +62085,7 @@
       name: "Mac Builder reclient staging"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
-      dimensions: "cores:12"
-      dimensions: "cpu:x86-64"
+      dimensions: "cpu:arm64"
       dimensions: "free_space:standard"
       dimensions: "os:Mac-14"
       dimensions: "pool:luci.chromium.ci"
@@ -62176,8 +62175,7 @@
       name: "Mac Builder reclient staging untrusted"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
-      dimensions: "cores:12"
-      dimensions: "cpu:x86-64"
+      dimensions: "cpu:arm64"
       dimensions: "free_space:standard"
       dimensions: "os:Mac-14"
       dimensions: "pool:luci.chromium.ci"
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl
index 256d984..07fe7c42 100644
--- a/infra/config/generated/testing/variants.pyl
+++ b/infra/config/generated/testing/variants.pyl
@@ -267,16 +267,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 127.0.6488.0',
+    'description': 'Run with ash-chrome version 127.0.6489.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v127.0.6488.0',
-          'revision': 'version:127.0.6488.0',
+          'location': 'lacros_version_skew_tests_v127.0.6489.0',
+          'revision': 'version:127.0.6489.0',
         },
       ],
     },
diff --git a/infra/config/subprojects/reclient/reclient.star b/infra/config/subprojects/reclient/reclient.star
index 27f4efa1..40d28f9 100644
--- a/infra/config/subprojects/reclient/reclient.star
+++ b/infra/config/subprojects/reclient/reclient.star
@@ -269,11 +269,12 @@
         ),
     ),
     gn_args = gn_args.config(
-        configs = ["gpu_tests", "release_builder", "reclient", "minimal_symbols"],
+        configs = ["gpu_tests", "release_builder", "reclient", "minimal_symbols", "x64"],
     ),
     builderless = True,
-    cores = 12,
+    cores = None,
     os = os.MAC_DEFAULT,
+    cpu = cpu.ARM64,
     console_view_category = "mac",
     priority = 35,
     reclient_bootstrap_env = {
diff --git a/infra/config/targets/lacros-version-skew-variants.json b/infra/config/targets/lacros-version-skew-variants.json
index 87a55bfb..7e77b43 100644
--- a/infra/config/targets/lacros-version-skew-variants.json
+++ b/infra/config/targets/lacros-version-skew-variants.json
@@ -1,16 +1,16 @@
 {
   "LACROS_VERSION_SKEW_CANARY": {
     "args": [
-      "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome"
+      "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome"
     ],
-    "description": "Run with ash-chrome version 127.0.6488.0",
+    "description": "Run with ash-chrome version 127.0.6489.0",
     "identifier": "Lacros version skew testing ash canary",
     "swarming": {
       "cipd_packages": [
         {
           "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-          "location": "lacros_version_skew_tests_v127.0.6488.0",
-          "revision": "version:127.0.6488.0"
+          "location": "lacros_version_skew_tests_v127.0.6489.0",
+          "revision": "version:127.0.6489.0"
         }
       ]
     }
diff --git a/internal b/internal
index 342050d..112b684 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 342050dfbbbfe808eb799cd2fe817987cf111f57
+Subproject commit 112b68415b55a35a2381caeb859f598c52056c60
diff --git a/ios_internal b/ios_internal
index 393ae9df..0e513f1 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 393ae9df29e5989726851fbd9316cad37dfea065
+Subproject commit 0e513f182a3f4b9b77cde29641329b6f79c604a4
diff --git a/net/base/load_timing_info.h b/net/base/load_timing_info.h
index cab6804..a93f961 100644
--- a/net/base/load_timing_info.h
+++ b/net/base/load_timing_info.h
@@ -26,6 +26,8 @@
 //
 // The general order for events is:
 // request_start
+// service_worker_router_evaluation_start
+// service_worker_cache_lookup_start
 // service_worker_start_time
 // proxy_start
 // proxy_end
@@ -140,6 +142,14 @@
   // (http://www.w3.org/TR/resource-timing/) for Web-surfacing requests.
   base::TimeTicks request_start;
 
+  // The time immediately before ServiceWorker static routing API starts
+  // matching a request with the registered router rules.
+  base::TimeTicks service_worker_router_evaluation_start;
+
+  // The time immediately before ServiceWorker static routing API starts
+  // looking up the cache storage when "cache" is specified as its source.
+  base::TimeTicks service_worker_cache_lookup_start;
+
   // The time immediately before starting ServiceWorker. If the response is not
   // provided by the ServiceWorker, kept empty.
   // Corresponds to |workerStart| in
diff --git a/net/http/transport_security_state_static.pins b/net/http/transport_security_state_static.pins
index ca787767..278429a0 100644
--- a/net/http/transport_security_state_static.pins
+++ b/net/http/transport_security_state_static.pins
@@ -43,9 +43,9 @@
 #   hash function for preloaded entries again (we have already done so once).
 #
 
-# Last updated: 2024-05-18 13:01 UTC
+# Last updated: 2024-05-19 12:56 UTC
 PinsListTimestamp
-1716037270
+1716123368
 
 TestSPKI
 sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
diff --git a/net/http/transport_security_state_static_pins.json b/net/http/transport_security_state_static_pins.json
index 64200cb9..820ca8e1 100644
--- a/net/http/transport_security_state_static_pins.json
+++ b/net/http/transport_security_state_static_pins.json
@@ -31,7 +31,7 @@
 // the 'static_spki_hashes' and 'bad_static_spki_hashes' fields in 'pinsets'
 // refer to, and the timestamp at which the pins list was last updated.
 //
-// Last updated: 2024-05-18 13:01 UTC
+// Last updated: 2024-05-19 12:56 UTC
 //
 {
   "pinsets": [
diff --git a/services/network/public/cpp/load_timing_info_mojom_traits.cc b/services/network/public/cpp/load_timing_info_mojom_traits.cc
index a875d3a..448670cc 100644
--- a/services/network/public/cpp/load_timing_info_mojom_traits.cc
+++ b/services/network/public/cpp/load_timing_info_mojom_traits.cc
@@ -42,6 +42,10 @@
          data.ReadPushEnd(&out->push_end) &&
          data.ReadServiceWorkerStartTime(&out->service_worker_start_time) &&
          data.ReadServiceWorkerReadyTime(&out->service_worker_ready_time) &&
+         data.ReadServiceWorkerRouterEvaluationStart(
+             &out->service_worker_router_evaluation_start) &&
+         data.ReadServiceWorkerCacheLookupStart(
+             &out->service_worker_cache_lookup_start) &&
          data.ReadServiceWorkerFetchStart(&out->service_worker_fetch_start) &&
          data.ReadServiceWorkerRespondWithSettled(
              &out->service_worker_respond_with_settled);
diff --git a/services/network/public/cpp/load_timing_info_mojom_traits.h b/services/network/public/cpp/load_timing_info_mojom_traits.h
index 25abfa32..1e42725 100644
--- a/services/network/public/cpp/load_timing_info_mojom_traits.h
+++ b/services/network/public/cpp/load_timing_info_mojom_traits.h
@@ -131,6 +131,16 @@
     return obj.service_worker_fetch_start;
   }
 
+  static base::TimeTicks service_worker_router_evaluation_start(
+      const net::LoadTimingInfo& obj) {
+    return obj.service_worker_router_evaluation_start;
+  }
+
+  static base::TimeTicks service_worker_cache_lookup_start(
+      const net::LoadTimingInfo& obj) {
+    return obj.service_worker_cache_lookup_start;
+  }
+
   static base::TimeTicks service_worker_respond_with_settled(
       const net::LoadTimingInfo& obj) {
     return obj.service_worker_respond_with_settled;
diff --git a/services/network/public/mojom/load_timing_info.mojom b/services/network/public/mojom/load_timing_info.mojom
index 4b7cf361..c137810 100644
--- a/services/network/public/mojom/load_timing_info.mojom
+++ b/services/network/public/mojom/load_timing_info.mojom
@@ -51,4 +51,14 @@
   // If the response is not provided by the service worker, kept empty.
   // If respondWith() was not called, kept empty.
   mojo_base.mojom.TimeTicks service_worker_respond_with_settled;
+
+  // The time immediately before ServiceWorker static routing API starts
+  // matching a request with the registered router rules. If the API is not
+  // used, kept empty.
+  mojo_base.mojom.TimeTicks service_worker_router_evaluation_start;
+
+  // The time immediately before ServiceWorker static routing API starts
+  // looking up the cache storage when "cache" is specified as its source. If
+  // the API is not used, or the source is not "cache", kept empty.
+  mojo_base.mojom.TimeTicks service_worker_cache_lookup_start;
 };
diff --git a/storage/common/database/database_identifier.cc b/storage/common/database/database_identifier.cc
index 1cbcbd0..0a05c657 100644
--- a/storage/common/database/database_identifier.cc
+++ b/storage/common/database/database_identifier.cc
@@ -14,6 +14,7 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 #include "url/url_canon.h"
+#include "url/url_features.h"
 
 namespace storage {
 
@@ -149,13 +150,23 @@
 
   GURL url(scheme + "://" + hostname + "/");
 
-  if (!url.IsStandard())
-    hostname.clear();
-
   // If a url doesn't parse cleanly or doesn't round trip, reject it.
-  if (!url.is_valid() || url.scheme() != scheme || url.host() != hostname)
+  if (!url.is_valid() || url.scheme() != scheme) {
     return DatabaseIdentifier();
-
+  }
+  // Unless StandardCompliantNonSpecialSchemeURLParsing feature is enabled,
+  // url.host() always return an empty string for non-special URLs.
+  if ((url::IsUsingStandardCompliantNonSpecialSchemeURLParsing() ||
+       url.IsStandard()) &&
+      (url.host() != hostname)) {
+    return DatabaseIdentifier();
+  }
+  // Clear hostname for a non-special URL. This behavior existed before
+  // non-special URLs are properly supported, and we're keeping this for
+  // compatibility reasons.
+  if (!url.IsStandard()) {
+    hostname.clear();
+  }
   return DatabaseIdentifier(scheme, hostname, port, false /* unique */, false);
 }
 
diff --git a/storage/common/database/database_identifier_unittest.cc b/storage/common/database/database_identifier_unittest.cc
index 5899d03..5e47241 100644
--- a/storage/common/database/database_identifier_unittest.cc
+++ b/storage/common/database/database_identifier_unittest.cc
@@ -8,9 +8,11 @@
 
 #include <string>
 
+#include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/origin.h"
+#include "url/url_features.h"
 
 namespace storage {
 
@@ -183,7 +185,28 @@
   }
 }
 
-TEST(DatabaseIdentifierTest, ExtractOriginDataFromIdentifier) {
+// Non-special URLs behavior is affected by the
+// StandardCompliantNonSpecialSchemeURLParsing feature.
+// See https://crbug.com/40063064 for details.
+class DatabaseIdentifierParamTest : public testing::TestWithParam<bool> {
+ public:
+  DatabaseIdentifierParamTest()
+      : use_standard_compliant_non_special_scheme_url_parsing_(GetParam()) {
+    scoped_feature_list_.InitWithFeatureState(
+        url::kStandardCompliantNonSpecialSchemeURLParsing,
+        use_standard_compliant_non_special_scheme_url_parsing_);
+  }
+
+  bool use_standard_compliant_non_special_scheme_url_parsing_;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Use a parameterized test here to ensure that
+// StandardCompliantNonSpecialSchemeURLParsing feature doesn't change the
+// behavior of DatabaseIdentifier.
+TEST_P(DatabaseIdentifierParamTest, ExtractOriginDataFromIdentifier) {
   struct IdentifierTestCase {
     std::string str;
     std::string expected_scheme;
@@ -259,6 +282,8 @@
   }
 }
 
+INSTANTIATE_TEST_SUITE_P(All, DatabaseIdentifierParamTest, ::testing::Bool());
+
 static GURL GURLToAndFromOriginIdentifier(const GURL& origin_url) {
   std::string id = storage::GetIdentifierFromOrigin(origin_url);
   return storage::GetOriginURLFromIdentifier(id);
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index fe76fa816..181e414 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5487,9 +5487,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5499,8 +5499,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
@@ -5643,9 +5643,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5655,8 +5655,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index d8791bf..8db981c 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -19664,9 +19664,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -19676,8 +19676,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
@@ -19820,9 +19820,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -19832,8 +19832,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 04995d3..dc3ec78 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -41840,9 +41840,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -41851,8 +41851,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
@@ -41990,9 +41990,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -42001,8 +42001,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
@@ -43339,9 +43339,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -43351,8 +43351,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
@@ -43495,9 +43495,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -43507,8 +43507,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
@@ -44820,9 +44820,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -44831,8 +44831,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
@@ -44970,9 +44970,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -44981,8 +44981,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 0f9db4b..d5f1c91 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -15763,12 +15763,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -15778,8 +15778,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
@@ -15939,12 +15939,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 127.0.6488.0",
+        "description": "Run with ash-chrome version 127.0.6489.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -15954,8 +15954,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v127.0.6488.0",
-              "revision": "version:127.0.6488.0"
+              "location": "lacros_version_skew_tests_v127.0.6489.0",
+              "revision": "version:127.0.6489.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 256d984..07fe7c42 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -267,16 +267,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 127.0.6488.0',
+    'description': 'Run with ash-chrome version 127.0.6489.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6488.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v127.0.6489.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v127.0.6488.0',
-          'revision': 'version:127.0.6488.0',
+          'location': 'lacros_version_skew_tests_v127.0.6489.0',
+          'revision': 'version:127.0.6489.0',
         },
       ],
     },
diff --git a/third_party/blink/public/mojom/timing/resource_timing.mojom b/third_party/blink/public/mojom/timing/resource_timing.mojom
index 7b2872a..6284c47 100644
--- a/third_party/blink/public/mojom/timing/resource_timing.mojom
+++ b/third_party/blink/public/mojom/timing/resource_timing.mojom
@@ -7,6 +7,7 @@
 import "mojo/public/mojom/base/time.mojom";
 import "services/network/public/mojom/fetch_api.mojom";
 import "services/network/public/mojom/load_timing_info.mojom";
+import "services/network/public/mojom/service_worker_router_info.mojom";
 import "third_party/blink/public/mojom/fetch/fetch_api_request.mojom";
 
 // This struct holds the information from PerformanceServerTiming that needs
@@ -123,4 +124,10 @@
   // Holds the string corresponding to |contentType| in
   // PerformanceResourceTiming (https://w3c.github.io/resource-timing/).
   string content_type;
+
+  // Holds the source type of ServiceWorker static routing API. It keeps track
+  // of |matchedSourceType| and |finalSourceType|. This is a proposed field in
+  // https://github.com/WICG/service-worker-static-routing-api, not in the
+  // resource-timing standard yet.
+  network.mojom.ServiceWorkerRouterInfo? service_worker_router_info;
 };
diff --git a/third_party/blink/public/web/web_node.h b/third_party/blink/public/web/web_node.h
index c5005fd..41fc646 100644
--- a/third_party/blink/public/web/web_node.h
+++ b/third_party/blink/public/web/web_node.h
@@ -31,12 +31,14 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_NODE_H_
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_NODE_H_
 
+#include "base/functional/callback_helpers.h"
 #include "base/functional/function_ref.h"
 #include "cc/paint/element_id.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_private_ptr.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/public/web/web_dom_event.h"
 #include "v8/include/v8-forward.h"
 
 namespace blink {
@@ -55,6 +57,10 @@
 // reason, subclasses must not add any additional data members.
 class BLINK_EXPORT WebNode {
  public:
+  enum class EventType {
+    kSelectionchange,
+  };
+
   static WebNode FromDomNodeId(int dom_node_id);
 
   virtual ~WebNode();
@@ -126,6 +132,12 @@
 
   int GetDomNodeId() const;
 
+  // Adds a listener to this node.
+  // Returns a RAII object that removes the listener.
+  base::ScopedClosureRunner AddEventListener(
+      EventType event_type,
+      base::RepeatingCallback<void(WebDOMEvent)> handler);
+
   // Helper to downcast to `T`. Will fail with a CHECK() if converting to `T` is
   // not legal. The returned `T` will always be non-null if `this` is non-null.
   template <typename T>
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py b/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py
index 74925fd..c00cb36 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py
@@ -852,6 +852,10 @@
         exposure_conditional = expr_from_exposure(member.exposure)
         if not exposure_conditional.is_always_true:
             node = CxxLikelyIfNode(cond=exposure_conditional, body=node)
+            node.accumulate(
+                CodeGenAccumulator.require_include_headers([
+                    "third_party/blink/renderer/platform/runtime_enabled_features.h"
+                ]))
 
         body.append(node)
 
@@ -972,6 +976,10 @@
         conditional = expr_from_exposure(member.exposure)
         if not conditional.is_always_true:
             node = CxxLikelyIfNode(cond=conditional, body=node)
+            node.accumulate(
+                CodeGenAccumulator.require_include_headers([
+                    "third_party/blink/renderer/platform/runtime_enabled_features.h"
+                ]))
 
         body.append(node)
 
diff --git a/third_party/blink/renderer/core/exported/web_node.cc b/third_party/blink/renderer/core/exported/web_node.cc
index 1e2b41a..2e895ec 100644
--- a/third_party/blink/renderer/core/exported/web_node.cc
+++ b/third_party/blink/renderer/core/exported/web_node.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
 #include "third_party/blink/renderer/core/dom/node.h"
 #include "third_party/blink/renderer/core/dom/node_list.h"
 #include "third_party/blink/renderer/core/dom/static_node_list.h"
@@ -55,6 +56,7 @@
 #include "third_party/blink/renderer/core/layout/layout_box.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/wtf/wtf.h"
 
 namespace blink {
@@ -271,4 +273,53 @@
   return WebNode(Node::FromDomNodeId(dom_node_id));
 }
 
+base::ScopedClosureRunner WebNode::AddEventListener(
+    EventType event_type,
+    base::RepeatingCallback<void(WebDOMEvent)> handler) {
+  class EventListener : public NativeEventListener {
+   public:
+    EventListener(Node* node,
+                  base::RepeatingCallback<void(WebDOMEvent)> handler)
+        : node_(node), handler_(std::move(handler)) {}
+
+    void Invoke(ExecutionContext*, Event* event) override {
+      handler_.Run(WebDOMEvent(event));
+    }
+
+    void AddListener() {
+      node_->addEventListener(event_type_name(), this,
+                              /*use_capture=*/false);
+    }
+
+    void RemoveListener() {
+      node_->removeEventListener(event_type_name(), this,
+                                 /*use_capture=*/false);
+    }
+
+    void Trace(Visitor* visitor) const override {
+      NativeEventListener::Trace(visitor);
+      visitor->Trace(node_);
+    }
+
+   private:
+    const AtomicString& event_type_name() {
+      switch (event_type_) {
+        case EventType::kSelectionchange:
+          return event_type_names::kSelectionchange;
+      }
+      NOTREACHED_NORETURN();
+    }
+
+    Member<Node> node_;
+    EventType event_type_;
+    base::RepeatingCallback<void(WebDOMEvent)> handler_;
+  };
+
+  WebPrivatePtrForGC<EventListener> listener =
+      MakeGarbageCollected<EventListener>(Unwrap<Node>(), std::move(handler));
+  listener->AddListener();
+  return base::ScopedClosureRunner(WTF::BindOnce(
+      &EventListener::RemoveListener, WrapWeakPersistent(listener.Get())));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_node_test.cc b/third_party/blink/renderer/core/exported/web_node_test.cc
index 004a2ca..70d76c3e 100644
--- a/third_party/blink/renderer/core/exported/web_node_test.cc
+++ b/third_party/blink/renderer/core/exported/web_node_test.cc
@@ -5,7 +5,10 @@
 #include "third_party/blink/public/web/web_node.h"
 
 #include <memory>
+
+#include "base/test/mock_callback.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/web/web_dom_event.h"
 #include "third_party/blink/public/web/web_element.h"
 #include "third_party/blink/public/web/web_element_collection.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
@@ -24,6 +27,14 @@
     GetDocument().documentElement()->setInnerHTML(html);
   }
 
+  void AddScript(String js) {
+    GetDocument().GetSettings()->SetScriptEnabled(true);
+    Element* script = GetDocument().CreateRawElement(html_names::kScriptTag);
+    script->setInnerHTML(js);
+    GetDocument().body()->AppendChild(script);
+    GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
+  }
+
   WebNode Root() { return WebNode(GetDocument().documentElement()); }
 };
 
@@ -181,4 +192,47 @@
                   .IsEmpty());
 }
 
+// Tests that AddEventListener() registers and deregisters a listener.
+TEST_F(WebNodeTest, AddEventListener) {
+  testing::MockFunction<void(std::string_view)> checkpoint;
+  base::MockRepeatingCallback<void(blink::WebDOMEvent)> handler;
+  {
+    testing::InSequence seq;
+    EXPECT_CALL(checkpoint, Call("focus"));
+    EXPECT_CALL(checkpoint, Call("set_caret 1"));
+    EXPECT_CALL(handler, Run);
+    EXPECT_CALL(checkpoint, Call("set_caret 2"));
+    EXPECT_CALL(handler, Run);
+    EXPECT_CALL(checkpoint, Call("set_caret 3"));
+  }
+
+  SetInnerHTML("<textarea id=field>0123456789</textarea>");
+
+  // Focuses the textarea.
+  auto focus = [&]() {
+    checkpoint.Call("focus");
+    AddScript(String("document.getElementById('field').focus()"));
+    task_environment().RunUntilIdle();
+  };
+
+  // Moves the caret in the field and fires a selectionchange event.
+  auto set_caret = [&](int caret_position) {
+    checkpoint.Call(base::StringPrintf("set_caret %d", caret_position));
+    AddScript(String(base::StringPrintf(
+        "document.getElementById('field').setSelectionRange(%d, %d)",
+        caret_position, caret_position)));
+    task_environment().RunUntilIdle();
+  };
+
+  focus();
+  {
+    auto remove_listener = Root().AddEventListener(
+        WebNode::EventType::kSelectionchange, handler.Get());
+    set_caret(1);
+    set_caret(2);
+    // The listener is removed by `remove_listener`'s destructor.
+  }
+  set_caret(3);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/inline/line_breaker.cc b/third_party/blink/renderer/core/layout/inline/line_breaker.cc
index 029310e..71b1935 100644
--- a/third_party/blink/renderer/core/layout/inline/line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/inline/line_breaker.cc
@@ -924,6 +924,7 @@
   if (ruby_break_token_) {
     HandleRuby(ruby_break_token_, line_info);
     ruby_break_token_ = nullptr;
+    HandleOverflowIfNeeded(line_info);
   }
 
   while (state_ != LineBreakState::kDone) {
diff --git a/third_party/blink/renderer/core/layout/inline/line_breaker_test.cc b/third_party/blink/renderer/core/layout/inline/line_breaker_test.cc
index 03865c8..60677d8b 100644
--- a/third_party/blink/renderer/core/layout/inline/line_breaker_test.cc
+++ b/third_party/blink/renderer/core/layout/inline/line_breaker_test.cc
@@ -1176,6 +1176,21 @@
   EXPECT_GE(line_info.Results()[1].ruby_column->base_line.EndTextOffset(), 79u);
 }
 
+// crbug.com/341142174 A crash with an overflowing continuation ruby column.
+TEST_F(LineBreakerTest, OverflowingContinuationRuby) {
+  InlineNode node = CreateInlineNode(R"HTML(
+<div id="container" style="width:1px; font-variant:small-caps;">
+<ruby>
+<q>
+AxBxC AxBxC
+</q>
+<rt>C b
+C AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+</ruby>)HTML");
+  ComputeMinMaxSizes(node);
+  // This test passes if no CHECK failures.
+}
+
 struct CanBreakInsideTestData {
   bool can_break_insde;
   const char* html;
diff --git a/third_party/blink/renderer/core/loader/cross_thread_resource_timing_info_copier.cc b/third_party/blink/renderer/core/loader/cross_thread_resource_timing_info_copier.cc
index 07113bc..0ace91a 100644
--- a/third_party/blink/renderer/core/loader/cross_thread_resource_timing_info_copier.cc
+++ b/third_party/blink/renderer/core/loader/cross_thread_resource_timing_info_copier.cc
@@ -34,7 +34,10 @@
       info->did_reuse_connection, info->is_secure_transport,
       info->allow_timing_details, info->allow_negative_values,
       CloneServerTimingInfoArray(info->server_timing),
-      info->render_blocking_status, info->response_status, info->content_type);
+      info->render_blocking_status, info->response_status, info->content_type,
+      info->service_worker_router_info
+          ? info->service_worker_router_info->Clone()
+          : nullptr);
 }
 
 CrossThreadCopier<blink::mojom::blink::ServerTimingInfoPtr>::Type
diff --git a/third_party/blink/renderer/core/timing/performance_resource_timing.cc b/third_party/blink/renderer/core/timing/performance_resource_timing.cc
index 1ceb611..e625f35f 100644
--- a/third_party/blink/renderer/core/timing/performance_resource_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_resource_timing.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/timing/performance_resource_timing.h"
 
 #include "base/notreached.h"
+#include "services/network/public/mojom/service_worker_router_info.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/timing/performance_mark_or_measure.mojom-blink.h"
 #include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink-forward.h"
@@ -53,6 +54,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.h"
+#include "third_party/blink/renderer/platform/loader/fetch/service_worker_router_info.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -175,6 +177,49 @@
       info_->allow_negative_values, CrossOriginIsolatedCapability());
 }
 
+DOMHighResTimeStamp PerformanceResourceTiming::workerRouterEvaluationStart()
+    const {
+  if (!info_->timing ||
+      info_->timing->service_worker_router_evaluation_start.is_null()) {
+    return 0.0;
+  }
+
+  return Performance::MonotonicTimeToDOMHighResTimeStamp(
+      TimeOrigin(), info_->timing->service_worker_router_evaluation_start,
+      info_->allow_negative_values, CrossOriginIsolatedCapability());
+}
+
+DOMHighResTimeStamp PerformanceResourceTiming::workerCacheLookupStart() const {
+  if (!info_->timing ||
+      info_->timing->service_worker_cache_lookup_start.is_null()) {
+    return 0.0;
+  }
+
+  return Performance::MonotonicTimeToDOMHighResTimeStamp(
+      TimeOrigin(), info_->timing->service_worker_cache_lookup_start,
+      info_->allow_negative_values, CrossOriginIsolatedCapability());
+}
+
+AtomicString PerformanceResourceTiming::matchedSourceType() const {
+  if (!info_->service_worker_router_info ||
+      !info_->service_worker_router_info->matched_source_type) {
+    return AtomicString();
+  }
+
+  return AtomicString(ServiceWorkerRouterInfo::GetRouterSourceTypeString(
+      *info_->service_worker_router_info->matched_source_type));
+}
+
+AtomicString PerformanceResourceTiming::finalSourceType() const {
+  if (!info_->service_worker_router_info ||
+      !info_->service_worker_router_info->actual_source_type) {
+    return AtomicString();
+  }
+
+  return AtomicString(ServiceWorkerRouterInfo::GetRouterSourceTypeString(
+      *info_->service_worker_router_info->actual_source_type));
+}
+
 DOMHighResTimeStamp PerformanceResourceTiming::WorkerReady() const {
   if (!info_->timing || info_->timing->service_worker_ready_time.is_null()) {
     return 0.0;
@@ -423,6 +468,13 @@
     builder.AddString("contentType", contentType());
   }
   builder.AddNumber("workerStart", workerStart());
+  if (RuntimeEnabledFeatures::ServiceWorkerStaticRouterTimingInfoEnabled()) {
+    builder.AddNumber("workerRouterEvaluationStart",
+                      workerRouterEvaluationStart());
+    builder.AddNumber("workerCacheLookupStart", workerCacheLookupStart());
+    builder.AddString("matchedSourceType", matchedSourceType());
+    builder.AddString("finalSourceType", finalSourceType());
+  }
   builder.AddNumber("redirectStart", redirectStart());
   builder.AddNumber("redirectEnd", redirectEnd());
   builder.AddNumber("fetchStart", fetchStart());
diff --git a/third_party/blink/renderer/core/timing/performance_resource_timing.h b/third_party/blink/renderer/core/timing/performance_resource_timing.h
index 19ee795..1f8d2d18 100644
--- a/third_party/blink/renderer/core/timing/performance_resource_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_resource_timing.h
@@ -72,6 +72,8 @@
   virtual AtomicString renderBlockingStatus() const;
   virtual AtomicString contentType() const;
   DOMHighResTimeStamp workerStart() const;
+  DOMHighResTimeStamp workerRouterEvaluationStart() const;
+  DOMHighResTimeStamp workerCacheLookupStart() const;
   virtual DOMHighResTimeStamp redirectStart() const;
   virtual DOMHighResTimeStamp redirectEnd() const;
   virtual DOMHighResTimeStamp fetchStart() const;
@@ -89,6 +91,8 @@
   virtual uint64_t decodedBodySize() const;
   uint16_t responseStatus() const;
   const HeapVector<Member<PerformanceServerTiming>>& serverTiming() const;
+  AtomicString matchedSourceType() const;
+  AtomicString finalSourceType() const;
 
   void Trace(Visitor*) const override;
 
diff --git a/third_party/blink/renderer/core/timing/performance_resource_timing.idl b/third_party/blink/renderer/core/timing/performance_resource_timing.idl
index ab7370d..9da8cd6 100644
--- a/third_party/blink/renderer/core/timing/performance_resource_timing.idl
+++ b/third_party/blink/renderer/core/timing/performance_resource_timing.idl
@@ -41,6 +41,14 @@
     readonly attribute DOMString nextHopProtocol;
     [Measure] readonly attribute DOMString deliveryType;
     readonly attribute DOMHighResTimeStamp workerStart;
+    [RuntimeEnabled=ServiceWorkerStaticRouterTimingInfo]
+        readonly attribute DOMHighResTimeStamp workerRouterEvaluationStart;
+    [RuntimeEnabled=ServiceWorkerStaticRouterTimingInfo]
+        readonly attribute DOMHighResTimeStamp workerCacheLookupStart;
+    [RuntimeEnabled=ServiceWorkerStaticRouterTimingInfo]
+        readonly attribute DOMString matchedSourceType;
+    [RuntimeEnabled=ServiceWorkerStaticRouterTimingInfo]
+        readonly attribute DOMString finalSourceType;
     readonly attribute DOMHighResTimeStamp redirectStart;
     readonly attribute DOMHighResTimeStamp redirectEnd;
     readonly attribute DOMHighResTimeStamp fetchStart;
diff --git a/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc b/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc
index 6c9e6ba4..0f7afd7 100644
--- a/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc
+++ b/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc
@@ -43,6 +43,7 @@
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
 #include "third_party/blink/renderer/modules/crypto/crypto_key.h"
 #include "third_party/blink/renderer/modules/crypto/crypto_utilities.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_change_record.idl b/third_party/blink/renderer/modules/file_system_access/file_system_change_record.idl
index 038fdc3..e3bada3 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_change_record.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_change_record.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// TODO(https://crbug.com/1019297): Include spec link.
+// TODO(https://crbug.com/320563255): Include spec link.
 
 enum FileSystemChangeType {
   "created",
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_observer.cc b/third_party/blink/renderer/modules/file_system_access/file_system_observer.cc
index 014a68d..325b755 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_observer.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_observer.cc
@@ -78,7 +78,7 @@
     ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // TODO(https://crbug.com/1489033): Add AllowStorageAccess checks.
+  // TODO(https://crbug.com/321980226): Add AllowStorageAccess checks.
 
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver<IDLUndefined>>(
       script_state, exception_state.GetContext());
@@ -116,7 +116,7 @@
     return;
   }
 
-  // TODO(https://crbug.com/1489029): Unqueue and pause records for this
+  // TODO(https://crbug.com/321980469): Unqueue and pause records for this
   // observation, or consider making observe() return a token which can be
   // passed to this method.
 
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_observer.idl b/third_party/blink/renderer/modules/file_system_access/file_system_observer.idl
index 87e0dcf..ae7e3fe 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_observer.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_observer.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// TODO(https://crbug.com/1019297): Include spec link.
+// TODO(https://crbug.com/320563255): Include spec link.
 // TODO(https://crbug.com/1444874): Measure usage.
 
 [
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_observer_callback.idl b/third_party/blink/renderer/modules/file_system_access/file_system_observer_callback.idl
index 0111523..c220b678 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_observer_callback.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_observer_callback.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// TODO(https://crbug.com/1019297): Include spec link.
+// TODO(https://crbug.com/320563255): Include spec link.
 
 callback FileSystemObserverCallback = void (
   sequence<FileSystemChangeRecord> records,
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_observer_observe_options.idl b/third_party/blink/renderer/modules/file_system_access/file_system_observer_observe_options.idl
index da450db..00fba5f9 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_observer_observe_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_observer_observe_options.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// TODO(https://crbug.com/1019297): Include spec link.
+// TODO(https://crbug.com/320563255): Include spec link.
 
 dictionary FileSystemObserverObserveOptions {
   boolean recursive = false;
diff --git a/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc b/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc
index 0cecb565..1a0035e9 100644
--- a/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc
+++ b/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
@@ -152,6 +153,12 @@
   g_runtime_call_stats_for_testing = nullptr;
 }
 
+// This function exists to remove runtime_enabled_features.h dependnency from
+// runtime_call_stats.h.
+bool RuntimeCallStats::IsEnabled() {
+  return RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled();
+}
+
 #if BUILDFLAG(RCS_COUNT_EVERYTHING)
 RuntimeCallCounter* RuntimeCallStats::GetCounter(const char* name) {
   CounterMap::iterator it = counter_map_.find(name);
diff --git a/third_party/blink/renderer/platform/bindings/runtime_call_stats.h b/third_party/blink/renderer/platform/bindings/runtime_call_stats.h
index f09f312..4667cc7 100644
--- a/third_party/blink/renderer/platform/bindings/runtime_call_stats.h
+++ b/third_party/blink/renderer/platform/bindings/runtime_call_stats.h
@@ -15,7 +15,6 @@
 #include "base/time/time.h"
 #include "third_party/blink/renderer/platform/bindings/buildflags.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "v8/include/v8.h"
@@ -123,27 +122,27 @@
 
 // Macros that take RuntimeCallStats as a parameter; used only in
 // RuntimeCallStatsTest.
-#define RUNTIME_CALL_STATS_ENTER_WITH_RCS(runtime_call_stats, timer,      \
-                                          counterId)                      \
-  if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \
-    (runtime_call_stats)->Enter(timer, counterId);                        \
+#define RUNTIME_CALL_STATS_ENTER_WITH_RCS(runtime_call_stats, timer, \
+                                          counterId)                 \
+  if (UNLIKELY(RuntimeCallStats::IsEnabled())) {                     \
+    (runtime_call_stats)->Enter(timer, counterId);                   \
   }
 
-#define RUNTIME_CALL_STATS_LEAVE_WITH_RCS(runtime_call_stats, timer)      \
-  if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \
-    (runtime_call_stats)->Leave(timer);                                   \
+#define RUNTIME_CALL_STATS_LEAVE_WITH_RCS(runtime_call_stats, timer) \
+  if (UNLIKELY(RuntimeCallStats::IsEnabled())) {                     \
+    (runtime_call_stats)->Leave(timer);                              \
   }
 
-#define RUNTIME_CALL_TIMER_SCOPE_WITH_RCS(runtime_call_stats, counterId)  \
-  std::optional<RuntimeCallTimerScope> rcs_scope;                         \
-  if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \
-    rcs_scope.emplace(runtime_call_stats, counterId);                     \
+#define RUNTIME_CALL_TIMER_SCOPE_WITH_RCS(runtime_call_stats, counterId) \
+  std::optional<RuntimeCallTimerScope> rcs_scope;                        \
+  if (UNLIKELY(RuntimeCallStats::IsEnabled())) {                         \
+    rcs_scope.emplace(runtime_call_stats, counterId);                    \
   }
 
-#define RUNTIME_CALL_TIMER_SCOPE_WITH_OPTIONAL_RCS(                       \
-    optional_scope_name, runtime_call_stats, counterId)                   \
-  if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \
-    optional_scope_name.emplace(runtime_call_stats, counterId);           \
+#define RUNTIME_CALL_TIMER_SCOPE_WITH_OPTIONAL_RCS(             \
+    optional_scope_name, runtime_call_stats, counterId)         \
+  if (UNLIKELY(RuntimeCallStats::IsEnabled())) {                \
+    optional_scope_name.emplace(runtime_call_stats, counterId); \
   }
 
 // Use the macros below instead of directly using RuntimeCallStats::Enter,
@@ -339,6 +338,8 @@
 
   const base::TickClock* clock() const { return clock_; }
 
+  static bool IsEnabled();
+
  private:
   raw_ptr<RuntimeCallTimer> current_timer_ = nullptr;
   bool in_use_ = false;
@@ -392,8 +393,9 @@
 
  public:
   explicit RuntimeCallStatsScopedTracer(v8::Isolate* isolate) {
-    if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled()))
+    if (UNLIKELY(RuntimeCallStats::IsEnabled())) {
       AddBeginTraceEventIfEnabled(isolate);
+    }
   }
 
   ~RuntimeCallStatsScopedTracer() {
diff --git a/third_party/blink/renderer/platform/exported/web_url_response.cc b/third_party/blink/renderer/platform/exported/web_url_response.cc
index 4a8b781..6977cb9 100644
--- a/third_party/blink/renderer/platform/exported/web_url_response.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_response.cc
@@ -76,7 +76,9 @@
       load_timing.push_end, load_timing.service_worker_start_time,
       load_timing.service_worker_ready_time,
       load_timing.service_worker_fetch_start,
-      load_timing.service_worker_respond_with_settled);
+      load_timing.service_worker_respond_with_settled,
+      load_timing.service_worker_router_evaluation_start,
+      load_timing.service_worker_cache_lookup_start);
 }
 
 // TODO(https://crbug.com/862940): Use KURL here.
@@ -319,6 +321,10 @@
   timing->SetConnectStart(mojo_timing.connect_timing.connect_start);
   timing->SetConnectEnd(mojo_timing.connect_timing.connect_end);
   timing->SetWorkerStart(mojo_timing.service_worker_start_time);
+  timing->SetWorkerRouterEvaluationStart(
+      mojo_timing.service_worker_router_evaluation_start);
+  timing->SetWorkerCacheLookupStart(
+      mojo_timing.service_worker_cache_lookup_start);
   timing->SetWorkerReady(mojo_timing.service_worker_ready_time);
   timing->SetWorkerFetchStart(mojo_timing.service_worker_fetch_start);
   timing->SetWorkerRespondWithSettled(
@@ -497,6 +503,9 @@
   auto info = ServiceWorkerRouterInfo::Create();
   info->SetRuleIdMatched(value.rule_id_matched);
   info->SetMatchedSourceType(value.matched_source_type);
+  info->SetActualSourceType(value.actual_source_type);
+  info->SetRouteRuleNum(value.route_rule_num);
+  info->SetEvaluationWorkerStatus(value.evaluation_worker_status);
   resource_response_->SetServiceWorkerRouterInfo(std::move(info));
 }
 
diff --git a/third_party/blink/renderer/platform/loader/BUILD.gn b/third_party/blink/renderer/platform/loader/BUILD.gn
index da50700..99c4902 100644
--- a/third_party/blink/renderer/platform/loader/BUILD.gn
+++ b/third_party/blink/renderer/platform/loader/BUILD.gn
@@ -274,6 +274,12 @@
   sources = [
     "testing/bytes_consumer_test_reader.cc",
     "testing/bytes_consumer_test_reader.h",
+    "testing/fake_background_resource_fetch_assets.cc",
+    "testing/fake_background_resource_fetch_assets.h",
+    "testing/fake_resource_load_info_notifier.cc",
+    "testing/fake_resource_load_info_notifier.h",
+    "testing/fake_url_loader_factory_for_background_thread.cc",
+    "testing/fake_url_loader_factory_for_background_thread.h",
     "testing/fetch_testing_platform_support.cc",
     "testing/fetch_testing_platform_support.h",
     "testing/mock_fetch_context.h",
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc
index cb1619a1..9bdebdcb 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -1090,6 +1090,15 @@
     loader_->DidChangePriority(load_priority, intra_priority_value);
 }
 
+void Resource::UpdateResourceWidth(const AtomicString& resource_width) {
+  if (resource_width) {
+    resource_request_.SetHttpHeaderField(AtomicString("sec-ch-width"),
+                                         resource_width);
+  } else {
+    resource_request_.ClearHttpHeaderField(AtomicString("sec-ch-width"));
+  }
+}
+
 // TODO(toyoshim): Consider to generate automatically. https://crbug.com/675515.
 static const char* InitiatorTypeNameToString(
     const AtomicString& initiator_type_name) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.h b/third_party/blink/renderer/platform/loader/fetch/resource.h
index 0969292..9d8f527 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -194,6 +194,8 @@
 
   void DidChangePriority(ResourceLoadPriority, int intra_priority_value);
 
+  void UpdateResourceWidth(const AtomicString& resource_width);
+
   // Returns two priorities:
   // - `first` is the priority with the fix of https://crbug.com/1369823.
   // - `second` is the priority without the fix, ignoring the priority from
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index 574e762..723f3bf 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -1382,6 +1382,10 @@
     resource->DidChangePriority(resource_request.Priority(), 0);
   }
 
+  // The resource width can change after the request was initially created.
+  resource->UpdateResourceWidth(
+      resource_request.HttpHeaderField(AtomicString("sec-ch-width")));
+
   // If only the fragment identifiers differ, it is the same resource.
   DCHECK(EqualIgnoringFragmentIdentifier(resource->Url(), params.Url()));
   if (policy == RevalidationPolicy::kUse &&
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc b/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc
index 48fbf4b..46e222a 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc
@@ -24,7 +24,8 @@
           send_start_, send_end_, receive_headers_start_, receive_headers_end_,
           receive_non_informational_headers_start_, receive_early_hints_start_,
           push_start_, push_end_, worker_start_, worker_ready_,
-          worker_fetch_start_, worker_respond_with_settled_);
+          worker_fetch_start_, worker_respond_with_settled_,
+          worker_router_evaluation_start_, worker_cache_lookup_start_);
   return timing;
 }
 
@@ -75,6 +76,16 @@
   worker_respond_with_settled_ = worker_respond_with_settled;
 }
 
+void ResourceLoadTiming::SetWorkerRouterEvaluationStart(
+    base::TimeTicks worker_router_evluation_start) {
+  worker_router_evaluation_start_ = worker_router_evluation_start;
+}
+
+void ResourceLoadTiming::SetWorkerCacheLookupStart(
+    base::TimeTicks worker_cache_lookup_start) {
+  worker_cache_lookup_start_ = worker_cache_lookup_start;
+}
+
 void ResourceLoadTiming::SetSendStart(base::TimeTicks send_start) {
   send_start_ = send_start;
 }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h b/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h
index 3c2f242..e498294 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h
@@ -52,6 +52,8 @@
   void SetWorkerReady(base::TimeTicks);
   void SetWorkerFetchStart(base::TimeTicks);
   void SetWorkerRespondWithSettled(base::TimeTicks);
+  void SetWorkerRouterEvaluationStart(base::TimeTicks);
+  void SetWorkerCacheLookupStart(base::TimeTicks);
   void SetSendStart(base::TimeTicks);
   void SetSendEnd(base::TimeTicks);
   void SetReceiveHeadersStart(base::TimeTicks);
@@ -73,6 +75,12 @@
   base::TimeTicks ConnectStart() const { return connect_start_; }
   base::TimeTicks ConnectEnd() const { return connect_end_; }
   base::TimeTicks WorkerStart() const { return worker_start_; }
+  base::TimeTicks WorkerRouterEvaluationStart() const {
+    return worker_router_evaluation_start_;
+  }
+  base::TimeTicks WorkerCacheLokupStart() const {
+    return worker_cache_lookup_start_;
+  }
   base::TimeTicks WorkerReady() const { return worker_ready_; }
   base::TimeTicks WorkerFetchStart() const { return worker_fetch_start_; }
   base::TimeTicks WorkerRespondWithSettled() const {
@@ -122,6 +130,8 @@
   base::TimeTicks worker_ready_;
   base::TimeTicks worker_fetch_start_;
   base::TimeTicks worker_respond_with_settled_;
+  base::TimeTicks worker_router_evaluation_start_;
+  base::TimeTicks worker_cache_lookup_start_;
   base::TimeTicks send_start_;
   base::TimeTicks send_end_;
   base::TimeTicks receive_headers_start_;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.cc b/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.cc
index 29ea11dd..54b4bcc 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_utils.h"
+
 #include "base/containers/contains.h"
 #include "base/notreached.h"
 #include "base/time/time.h"
@@ -12,6 +13,7 @@
 #include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h"
 #include "third_party/blink/renderer/platform/loader/fetch/delivery_type_names.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h"
+#include "third_party/blink/renderer/platform/loader/fetch/service_worker_router_info.h"
 #include "third_party/blink/renderer/platform/network/http_names.h"
 #include "third_party/blink/renderer/platform/network/http_parsers.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -85,6 +87,11 @@
     }
   }
 
+  info->service_worker_router_info =
+      response->GetServiceWorkerRouterInfo()
+          ? response->GetServiceWorkerRouterInfo()->ToMojo()
+          : nullptr;
+
   bool allow_response_details = response->IsCorsSameOrigin();
 
   info->content_type = g_empty_string;
diff --git a/third_party/blink/renderer/platform/loader/fetch/service_worker_router_info.cc b/third_party/blink/renderer/platform/loader/fetch/service_worker_router_info.cc
index 3b30bc6..dbf7386 100644
--- a/third_party/blink/renderer/platform/loader/fetch/service_worker_router_info.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/service_worker_router_info.cc
@@ -15,12 +15,29 @@
   return base::AdoptRef(new ServiceWorkerRouterInfo);
 }
 
+String ServiceWorkerRouterInfo::GetRouterSourceTypeString(
+    const network::mojom::ServiceWorkerRouterSourceType source) {
+  switch (source) {
+    case network::mojom::ServiceWorkerRouterSourceType::kNetwork:
+      return "network";
+    case network::mojom::ServiceWorkerRouterSourceType::kRace:
+      return "race-network-and-fetch";
+    case network::mojom::ServiceWorkerRouterSourceType::kCache:
+      return "cache";
+    case network::mojom::ServiceWorkerRouterSourceType::kFetchEvent:
+      return "fetch-event";
+  }
+}
+
 network::mojom::blink::ServiceWorkerRouterInfoPtr
 ServiceWorkerRouterInfo::ToMojo() const {
   network::mojom::blink::ServiceWorkerRouterInfoPtr info =
       network::mojom::blink::ServiceWorkerRouterInfo::New();
   info->rule_id_matched = rule_id_matched_;
   info->matched_source_type = matched_source_type_;
+  info->actual_source_type = actual_source_type_;
+  info->route_rule_num = route_rule_num_;
+  info->evaluation_worker_status = evaluation_worker_status_;
   return info;
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/service_worker_router_info.h b/third_party/blink/renderer/platform/loader/fetch/service_worker_router_info.h
index e216547..a1021fd 100644
--- a/third_party/blink/renderer/platform/loader/fetch/service_worker_router_info.h
+++ b/third_party/blink/renderer/platform/loader/fetch/service_worker_router_info.h
@@ -11,6 +11,7 @@
 #include "third_party/blink/public/common/service_worker/service_worker_router_rule.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
@@ -19,6 +20,9 @@
  public:
   static scoped_refptr<ServiceWorkerRouterInfo> Create();
 
+  static String GetRouterSourceTypeString(
+      const network::mojom::ServiceWorkerRouterSourceType source);
+
   network::mojom::blink::ServiceWorkerRouterInfoPtr ToMojo() const;
 
   void SetRuleIdMatched(std::optional<std::uint32_t> rule_id_matched) {
@@ -40,12 +44,44 @@
     return matched_source_type_;
   }
 
+  void SetActualSourceType(
+      const std::optional<network::mojom::ServiceWorkerRouterSourceType>&
+          type) {
+    actual_source_type_ = type;
+  }
+
+  const std::optional<network::mojom::ServiceWorkerRouterSourceType>&
+  ActualSourceType() const {
+    return actual_source_type_;
+  }
+
+  std::uint64_t RouteRuleNum() const { return route_rule_num_; }
+
+  void SetRouteRuleNum(std::uint64_t route_rule_num) {
+    route_rule_num_ = route_rule_num;
+  }
+
+  const std::optional<network::mojom::ServiceWorkerStatus>&
+  EvaluationWorkerStatus() const {
+    return evaluation_worker_status_;
+  }
+
+  void SetEvaluationWorkerStatus(
+      const std::optional<network::mojom::ServiceWorkerStatus>&
+          evaluation_worker_status) {
+    evaluation_worker_status_ = evaluation_worker_status;
+  }
+
  private:
   ServiceWorkerRouterInfo();
 
   std::optional<uint32_t> rule_id_matched_;
   std::optional<network::mojom::ServiceWorkerRouterSourceType>
       matched_source_type_;
+  std::optional<network::mojom::ServiceWorkerRouterSourceType>
+      actual_source_type_;
+  uint64_t route_rule_num_;
+  std::optional<network::mojom::ServiceWorkerStatus> evaluation_worker_status_;
 };
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/background_url_loader_unittest.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/background_url_loader_unittest.cc
index 498d2e7..2e04d17 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/background_url_loader_unittest.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/background_url_loader_unittest.cc
@@ -39,6 +39,8 @@
 #include "third_party/blink/renderer/platform/loader/fetch/background_code_cache_host.h"
 #include "third_party/blink/renderer/platform/loader/fetch/url_loader/background_response_processor.h"
 #include "third_party/blink/renderer/platform/loader/fetch/url_loader/url_loader_client.h"
+#include "third_party/blink/renderer/platform/loader/testing/fake_background_resource_fetch_assets.h"
+#include "third_party/blink/renderer/platform/loader/testing/fake_url_loader_factory_for_background_thread.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
@@ -87,10 +89,6 @@
 constexpr int kMaxBufferedBytesPerProcess = 1000;
 constexpr std::string kTestBodyString = "test data.";
 
-using LoadStartCallback = base::OnceCallback<void(
-    mojo::PendingReceiver<network::mojom::URLLoader>,
-    mojo::PendingRemote<network::mojom::URLLoaderClient>)>;
-
 using MaybeStartFunction =
     CrossThreadOnceFunction<bool(network::mojom::URLResponseHeadPtr&,
                                  mojo::ScopedDataPipeConsumerHandle&,
@@ -297,113 +295,6 @@
   bool process_wide_count_updated_ = false;
 };
 
-class FakeURLLoaderFactory : public network::SharedURLLoaderFactory {
- public:
-  // This SharedURLLoaderFactory is cloned and passed to the background thread
-  // via PendingFactory. `load_start_callback` will be called in the background
-  // thread.
-  explicit FakeURLLoaderFactory(LoadStartCallback load_start_callback)
-      : load_start_callback_(std::move(load_start_callback)) {}
-  FakeURLLoaderFactory(const FakeURLLoaderFactory&) = delete;
-  FakeURLLoaderFactory& operator=(const FakeURLLoaderFactory&) = delete;
-  ~FakeURLLoaderFactory() override = default;
-
-  // network::SharedURLLoaderFactory:
-  void CreateLoaderAndStart(
-      mojo::PendingReceiver<network::mojom::URLLoader> loader,
-      int32_t request_id,
-      uint32_t options,
-      const network::ResourceRequest& request,
-      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
-      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
-      override {
-    CHECK(load_start_callback_);
-    std::move(load_start_callback_).Run(std::move(loader), std::move(client));
-  }
-  void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
-      override {
-    // Pass |this| as the receiver context to make sure this object stays alive
-    // while it still has receivers.
-    receivers_.Add(this, std::move(receiver), this);
-  }
-  std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override {
-    CHECK(load_start_callback_);
-    return std::make_unique<PendingFactory>(std::move(load_start_callback_));
-  }
-
- private:
-  class PendingFactory : public network::PendingSharedURLLoaderFactory {
-   public:
-    explicit PendingFactory(LoadStartCallback load_start_callback)
-        : load_start_callback_(std::move(load_start_callback)) {}
-    PendingFactory(const PendingFactory&) = delete;
-    PendingFactory& operator=(const PendingFactory&) = delete;
-    ~PendingFactory() override = default;
-
-   protected:
-    scoped_refptr<network::SharedURLLoaderFactory> CreateFactory() override {
-      CHECK(load_start_callback_);
-      return base::MakeRefCounted<FakeURLLoaderFactory>(
-          std::move(load_start_callback_));
-    }
-
-   private:
-    LoadStartCallback load_start_callback_;
-  };
-
-  mojo::ReceiverSet<network::mojom::URLLoaderFactory,
-                    scoped_refptr<FakeURLLoaderFactory>>
-      receivers_;
-  LoadStartCallback load_start_callback_;
-};
-
-class FakeBackgroundResourceFetchAssets
-    : public WebBackgroundResourceFetchAssets {
- public:
-  explicit FakeBackgroundResourceFetchAssets(
-      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-      LoadStartCallback load_start_callback)
-      : background_task_runner_(std::move(background_task_runner)),
-        pending_loader_factory_(base::MakeRefCounted<FakeURLLoaderFactory>(
-                                    std::move(load_start_callback))
-                                    ->Clone()) {}
-  FakeBackgroundResourceFetchAssets(const FakeBackgroundResourceFetchAssets&) =
-      delete;
-  FakeBackgroundResourceFetchAssets& operator=(
-      const FakeBackgroundResourceFetchAssets&) = delete;
-  ~FakeBackgroundResourceFetchAssets() override {
-    if (url_loader_factory_) {
-      // `url_loader_factory_` must be released in the background thread.
-      background_task_runner_->ReleaseSoon(FROM_HERE,
-                                           std::move(url_loader_factory_));
-    }
-  }
-
-  const scoped_refptr<base::SequencedTaskRunner>& GetTaskRunner() override {
-    return background_task_runner_;
-  }
-
-  scoped_refptr<network::SharedURLLoaderFactory> GetLoaderFactory() override {
-    if (!url_loader_factory_) {
-      url_loader_factory_ = network::SharedURLLoaderFactory::Create(
-          std::move(pending_loader_factory_));
-    }
-    return url_loader_factory_;
-  }
-
-  URLLoaderThrottleProvider* GetThrottleProvider() override { return nullptr; }
-  const blink::LocalFrameToken& GetLocalFrameToken() override {
-    return local_frame_token_;
-  }
-
- private:
-  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
-  std::unique_ptr<network::PendingSharedURLLoaderFactory>
-      pending_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  const blink::LocalFrameToken local_frame_token_;
-};
-
 class FakeURLLoaderClient : public URLLoaderClient {
  public:
   explicit FakeURLLoaderClient(
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/resource_request_sender_unittest.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/resource_request_sender_unittest.cc
index 5428918..75f2a30e 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/resource_request_sender_unittest.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/resource_request_sender_unittest.cc
@@ -48,6 +48,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/code_cache_host.h"
 #include "third_party/blink/renderer/platform/loader/fetch/url_loader/resource_request_client.h"
 #include "third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.h"
+#include "third_party/blink/renderer/platform/loader/testing/fake_url_loader_factory_for_background_thread.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
 #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
@@ -1669,74 +1670,6 @@
   EXPECT_TRUE(mock_client_->received_response());
 }
 
-class BackgroundThreadURLLoaderFactory
-    : public network::SharedURLLoaderFactory {
- public:
-  using LoadStartCallback = base::OnceCallback<void(
-      mojo::PendingReceiver<network::mojom::URLLoader>,
-      mojo::PendingRemote<network::mojom::URLLoaderClient>)>;
-
-  // This SharedURLLoaderFactory is cloned and passed to the background thread
-  // via PendingFactory. `load_start_callback` will be called in the background
-  // thread.
-  explicit BackgroundThreadURLLoaderFactory(
-      LoadStartCallback load_start_callback)
-      : load_start_callback_(std::move(load_start_callback)) {}
-  BackgroundThreadURLLoaderFactory(const BackgroundThreadURLLoaderFactory&) =
-      delete;
-  BackgroundThreadURLLoaderFactory& operator=(
-      const BackgroundThreadURLLoaderFactory&) = delete;
-  ~BackgroundThreadURLLoaderFactory() override = default;
-
-  // network::SharedURLLoaderFactory:
-  void CreateLoaderAndStart(
-      mojo::PendingReceiver<network::mojom::URLLoader> loader,
-      int32_t request_id,
-      uint32_t options,
-      const network::ResourceRequest& request,
-      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
-      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
-      override {
-    CHECK(load_start_callback_);
-    std::move(load_start_callback_).Run(std::move(loader), std::move(client));
-  }
-  void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
-      override {
-    // Pass |this| as the receiver context to make sure this object stays alive
-    // while it still has receivers.
-    receivers_.Add(this, std::move(receiver), this);
-  }
-  std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override {
-    CHECK(load_start_callback_);
-    return std::make_unique<PendingFactory>(std::move(load_start_callback_));
-  }
-
- private:
-  class PendingFactory : public network::PendingSharedURLLoaderFactory {
-   public:
-    explicit PendingFactory(LoadStartCallback load_start_callback)
-        : load_start_callback_(std::move(load_start_callback)) {}
-    PendingFactory(const PendingFactory&) = delete;
-    PendingFactory& operator=(const PendingFactory&) = delete;
-    ~PendingFactory() override = default;
-
-   protected:
-    scoped_refptr<network::SharedURLLoaderFactory> CreateFactory() override {
-      CHECK(load_start_callback_);
-      return base::MakeRefCounted<BackgroundThreadURLLoaderFactory>(
-          std::move(load_start_callback_));
-    }
-
-   private:
-    LoadStartCallback load_start_callback_;
-  };
-
-  mojo::ReceiverSet<network::mojom::URLLoaderFactory,
-                    scoped_refptr<BackgroundThreadURLLoaderFactory>>
-      receivers_;
-  LoadStartCallback load_start_callback_;
-};
-
 class ResourceRequestSenderSyncTest : public testing::Test {
  public:
   explicit ResourceRequestSenderSyncTest() = default;
@@ -1777,7 +1710,8 @@
   scoped_refptr<MockRequestClient> mock_client =
       base::MakeRefCounted<MockRequestClient>();
   auto loader_factory =
-      base::MakeRefCounted<BackgroundThreadURLLoaderFactory>(base::BindOnce(
+      base::MakeRefCounted<
+          FakeURLLoaderFactoryForBackgroundThread>(base::BindOnce(
           [](mojo::PendingReceiver<network::mojom::URLLoader> loader,
              mojo::PendingRemote<network::mojom::URLLoaderClient> client) {
             mojo::MakeSelfOwnedReceiver(std::make_unique<MockLoader>(),
@@ -1811,10 +1745,10 @@
         std::move(callback).Run({}, {});
       }));
 
-  auto loader_factory = base::MakeRefCounted<BackgroundThreadURLLoaderFactory>(
-      base::BindOnce([](mojo::PendingReceiver<network::mojom::URLLoader> loader,
-                        mojo::PendingRemote<network::mojom::URLLoaderClient>
-                            client) {
+  auto loader_factory = base::MakeRefCounted<
+      FakeURLLoaderFactoryForBackgroundThread>(base::BindOnce(
+      [](mojo::PendingReceiver<network::mojom::URLLoader> loader,
+         mojo::PendingRemote<network::mojom::URLLoaderClient> client) {
         std::unique_ptr<MockLoader> mock_loader =
             std::make_unique<MockLoader>();
         MockLoader* mock_loader_prt = mock_loader.get();
@@ -1875,10 +1809,10 @@
         std::move(callback).Run({"Foo-Bar", "Hoge-Piyo"}, {});
       }));
 
-  auto loader_factory = base::MakeRefCounted<BackgroundThreadURLLoaderFactory>(
-      base::BindOnce([](mojo::PendingReceiver<network::mojom::URLLoader> loader,
-                        mojo::PendingRemote<network::mojom::URLLoaderClient>
-                            client) {
+  auto loader_factory = base::MakeRefCounted<
+      FakeURLLoaderFactoryForBackgroundThread>(base::BindOnce(
+      [](mojo::PendingReceiver<network::mojom::URLLoader> loader,
+         mojo::PendingRemote<network::mojom::URLLoaderClient> client) {
         std::unique_ptr<MockLoader> mock_loader =
             std::make_unique<MockLoader>();
         MockLoader* mock_loader_prt = mock_loader.get();
@@ -1942,10 +1876,10 @@
         std::move(callback).Run({}, std::move(modified_headers));
       }));
 
-  auto loader_factory = base::MakeRefCounted<BackgroundThreadURLLoaderFactory>(
-      base::BindOnce([](mojo::PendingReceiver<network::mojom::URLLoader> loader,
-                        mojo::PendingRemote<network::mojom::URLLoaderClient>
-                            client) {
+  auto loader_factory = base::MakeRefCounted<
+      FakeURLLoaderFactoryForBackgroundThread>(base::BindOnce(
+      [](mojo::PendingReceiver<network::mojom::URLLoader> loader,
+         mojo::PendingRemote<network::mojom::URLLoaderClient> client) {
         std::unique_ptr<MockLoader> mock_loader =
             std::make_unique<MockLoader>();
         MockLoader* mock_loader_prt = mock_loader.get();
@@ -2007,7 +1941,8 @@
       }));
 
   auto loader_factory =
-      base::MakeRefCounted<BackgroundThreadURLLoaderFactory>(base::BindOnce(
+      base::MakeRefCounted<
+          FakeURLLoaderFactoryForBackgroundThread>(base::BindOnce(
           [](mojo::PendingReceiver<network::mojom::URLLoader> loader,
              mojo::PendingRemote<network::mojom::URLLoaderClient> client) {
             std::unique_ptr<MockLoader> mock_loader =
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc
index 3cd128a6..d6f18d5 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc
@@ -20,6 +20,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
 #include "third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_client.h"
+#include "third_party/blink/renderer/platform/loader/testing/fake_resource_load_info_notifier.h"
 #include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 
@@ -105,46 +106,6 @@
     mojo::Receiver<network::mojom::URLLoader> receiver_;
   };
 
-  class FakeResourceLoadInfoNotifier final
-      : public blink::mojom::ResourceLoadInfoNotifier {
-   public:
-    FakeResourceLoadInfoNotifier() = default;
-
-    FakeResourceLoadInfoNotifier(const FakeResourceLoadInfoNotifier&) = delete;
-    FakeResourceLoadInfoNotifier& operator=(
-        const FakeResourceLoadInfoNotifier&) = delete;
-
-    // blink::mojom::ResourceLoadInfoNotifier overrides.
-#if BUILDFLAG(IS_ANDROID)
-    void NotifyUpdateUserGestureCarryoverInfo() override {}
-#endif
-    void NotifyResourceRedirectReceived(
-        const net::RedirectInfo& redirect_info,
-        network::mojom::URLResponseHeadPtr redirect_response) override {}
-    void NotifyResourceResponseReceived(
-        int64_t request_id,
-        const url::SchemeHostPort& final_url,
-        network::mojom::URLResponseHeadPtr head,
-        network::mojom::RequestDestination request_destination,
-        bool is_ad_resource) override {}
-    void NotifyResourceTransferSizeUpdated(
-        int64_t request_id,
-        int32_t transfer_size_diff) override {}
-    void NotifyResourceLoadCompleted(
-        blink::mojom::ResourceLoadInfoPtr resource_load_info,
-        const ::network::URLLoaderCompletionStatus& status) override {
-      resource_load_info_ = std::move(resource_load_info);
-    }
-    void NotifyResourceLoadCanceled(int64_t request_id) override {}
-    void Clone(mojo::PendingReceiver<blink::mojom::ResourceLoadInfoNotifier>
-                   pending_resource_load_info_notifier) override {}
-
-    std::string GetMimeType() { return resource_load_info_->mime_type; }
-
-   private:
-    blink::mojom::ResourceLoadInfoPtr resource_load_info_;
-  };
-
   class MockResourceLoadObserver : public ResourceLoadObserver {
    public:
     MOCK_METHOD2(DidStartRequest, void(const FetchParameters&, ResourceType));
diff --git a/third_party/blink/renderer/platform/loader/testing/fake_background_resource_fetch_assets.cc b/third_party/blink/renderer/platform/loader/testing/fake_background_resource_fetch_assets.cc
new file mode 100644
index 0000000..188d985
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/testing/fake_background_resource_fetch_assets.cc
@@ -0,0 +1,48 @@
+// 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.
+
+#include "third_party/blink/renderer/platform/loader/testing/fake_background_resource_fetch_assets.h"
+
+#include "base/task/sequenced_task_runner.h"
+#include "third_party/blink/renderer/platform/loader/testing/fake_url_loader_factory_for_background_thread.h"
+
+namespace blink {
+
+FakeBackgroundResourceFetchAssets::FakeBackgroundResourceFetchAssets(
+    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+    LoadStartCallback load_start_callback)
+    : background_task_runner_(std::move(background_task_runner)),
+      pending_loader_factory_(
+          base::MakeRefCounted<FakeURLLoaderFactoryForBackgroundThread>(
+              std::move(load_start_callback))
+              ->Clone()) {}
+
+FakeBackgroundResourceFetchAssets::~FakeBackgroundResourceFetchAssets() {
+  if (url_loader_factory_) {
+    // `url_loader_factory_` must be released in the background thread.
+    background_task_runner_->ReleaseSoon(FROM_HERE,
+                                         std::move(url_loader_factory_));
+  }
+}
+
+const scoped_refptr<base::SequencedTaskRunner>&
+FakeBackgroundResourceFetchAssets::GetTaskRunner() {
+  return background_task_runner_;
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+FakeBackgroundResourceFetchAssets::GetLoaderFactory() {
+  if (!url_loader_factory_) {
+    url_loader_factory_ = network::SharedURLLoaderFactory::Create(
+        std::move(pending_loader_factory_));
+  }
+  return url_loader_factory_;
+}
+
+const blink::LocalFrameToken&
+FakeBackgroundResourceFetchAssets::GetLocalFrameToken() {
+  return local_frame_token_;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/testing/fake_background_resource_fetch_assets.h b/third_party/blink/renderer/platform/loader/testing/fake_background_resource_fetch_assets.h
new file mode 100644
index 0000000..a01e01b2
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/testing/fake_background_resource_fetch_assets.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FAKE_BACKGROUND_RESOURCE_FETCH_ASSETS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FAKE_BACKGROUND_RESOURCE_FETCH_ASSETS_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
+#include "third_party/blink/public/platform/web_background_resource_fetch_assets.h"
+
+namespace base {
+class SequencedTaskRunner;
+}  // namespace base
+
+namespace blink {
+
+// This class can be used for testing the behaviour of a fetch request with a
+// BackgroundURLLoader.
+class FakeBackgroundResourceFetchAssets
+    : public WebBackgroundResourceFetchAssets {
+ public:
+  using LoadStartCallback = base::OnceCallback<void(
+      mojo::PendingReceiver<network::mojom::URLLoader>,
+      mojo::PendingRemote<network::mojom::URLLoaderClient>)>;
+
+  // Constructs a FakeBackgroundResourceFetchAssets object.
+  // `load_start_callback` will be called on the `background_task_runner` when
+  // a fetch request is started.
+  FakeBackgroundResourceFetchAssets(
+      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+      LoadStartCallback load_start_callback);
+
+  FakeBackgroundResourceFetchAssets(const FakeBackgroundResourceFetchAssets&) =
+      delete;
+  FakeBackgroundResourceFetchAssets& operator=(
+      const FakeBackgroundResourceFetchAssets&) = delete;
+  ~FakeBackgroundResourceFetchAssets() override;
+
+  // WebBackgroundResourceFetchAssets implementation:
+  const scoped_refptr<base::SequencedTaskRunner>& GetTaskRunner() override;
+  scoped_refptr<network::SharedURLLoaderFactory> GetLoaderFactory() override;
+  URLLoaderThrottleProvider* GetThrottleProvider() override { return nullptr; }
+  const blink::LocalFrameToken& GetLocalFrameToken() override;
+
+ private:
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+  std::unique_ptr<network::PendingSharedURLLoaderFactory>
+      pending_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  const blink::LocalFrameToken local_frame_token_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FAKE_BACKGROUND_RESOURCE_FETCH_ASSETS_H_
diff --git a/third_party/blink/renderer/platform/loader/testing/fake_resource_load_info_notifier.cc b/third_party/blink/renderer/platform/loader/testing/fake_resource_load_info_notifier.cc
new file mode 100644
index 0000000..9db2506f
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/testing/fake_resource_load_info_notifier.cc
@@ -0,0 +1,25 @@
+// 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.
+
+#include "third_party/blink/renderer/platform/loader/testing/fake_resource_load_info_notifier.h"
+
+#include "services/network/public/mojom/url_response_head.mojom.h"
+#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
+
+namespace blink {
+
+FakeResourceLoadInfoNotifier::FakeResourceLoadInfoNotifier() = default;
+FakeResourceLoadInfoNotifier::~FakeResourceLoadInfoNotifier() = default;
+
+void FakeResourceLoadInfoNotifier::NotifyResourceLoadCompleted(
+    blink::mojom::ResourceLoadInfoPtr resource_load_info,
+    const ::network::URLLoaderCompletionStatus& status) {
+  resource_load_info_ = std::move(resource_load_info);
+}
+
+std::string FakeResourceLoadInfoNotifier::GetMimeType() {
+  return resource_load_info_->mime_type;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/testing/fake_resource_load_info_notifier.h b/third_party/blink/renderer/platform/loader/testing/fake_resource_load_info_notifier.h
new file mode 100644
index 0000000..938ccca
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/testing/fake_resource_load_info_notifier.h
@@ -0,0 +1,55 @@
+// 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FAKE_RESOURCE_LOAD_INFO_NOTIFIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FAKE_RESOURCE_LOAD_INFO_NOTIFIER_H_
+
+#include "build/build_config.h"
+#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-forward.h"
+#include "third_party/blink/public/mojom/loader/resource_load_info_notifier.mojom.h"
+
+namespace blink {
+
+// A fake implementation of blink::mojom::ResourceLoadInfoNotifier.
+class FakeResourceLoadInfoNotifier final
+    : public blink::mojom::ResourceLoadInfoNotifier {
+ public:
+  FakeResourceLoadInfoNotifier();
+  ~FakeResourceLoadInfoNotifier() override;
+
+  FakeResourceLoadInfoNotifier(const FakeResourceLoadInfoNotifier&) = delete;
+  FakeResourceLoadInfoNotifier& operator=(const FakeResourceLoadInfoNotifier&) =
+      delete;
+
+  // blink::mojom::ResourceLoadInfoNotifier overrides.
+#if BUILDFLAG(IS_ANDROID)
+  void NotifyUpdateUserGestureCarryoverInfo() override {}
+#endif
+  void NotifyResourceRedirectReceived(
+      const net::RedirectInfo& redirect_info,
+      network::mojom::URLResponseHeadPtr redirect_response) override {}
+  void NotifyResourceResponseReceived(
+      int64_t request_id,
+      const url::SchemeHostPort& final_url,
+      network::mojom::URLResponseHeadPtr head,
+      network::mojom::RequestDestination request_destination,
+      bool is_ad_resource) override {}
+  void NotifyResourceTransferSizeUpdated(int64_t request_id,
+                                         int32_t transfer_size_diff) override {}
+  void NotifyResourceLoadCompleted(
+      blink::mojom::ResourceLoadInfoPtr resource_load_info,
+      const ::network::URLLoaderCompletionStatus& status) override;
+  void NotifyResourceLoadCanceled(int64_t request_id) override {}
+  void Clone(mojo::PendingReceiver<blink::mojom::ResourceLoadInfoNotifier>
+                 pending_resource_load_info_notifier) override {}
+
+  std::string GetMimeType();
+
+ private:
+  blink::mojom::ResourceLoadInfoPtr resource_load_info_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FAKE_RESOURCE_LOAD_INFO_NOTIFIER_H_
diff --git a/third_party/blink/renderer/platform/loader/testing/fake_url_loader_factory_for_background_thread.cc b/third_party/blink/renderer/platform/loader/testing/fake_url_loader_factory_for_background_thread.cc
new file mode 100644
index 0000000..4b5543d
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/testing/fake_url_loader_factory_for_background_thread.cc
@@ -0,0 +1,61 @@
+// 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.
+
+#include "third_party/blink/renderer/platform/loader/testing/fake_url_loader_factory_for_background_thread.h"
+
+namespace blink {
+
+class FakeURLLoaderFactoryForBackgroundThread::PendingFactory
+    : public network::PendingSharedURLLoaderFactory {
+ public:
+  explicit PendingFactory(LoadStartCallback load_start_callback)
+      : load_start_callback_(std::move(load_start_callback)) {}
+  PendingFactory(const PendingFactory&) = delete;
+  PendingFactory& operator=(const PendingFactory&) = delete;
+  ~PendingFactory() override = default;
+
+ protected:
+  scoped_refptr<network::SharedURLLoaderFactory> CreateFactory() override {
+    CHECK(load_start_callback_);
+    return base::MakeRefCounted<FakeURLLoaderFactoryForBackgroundThread>(
+        std::move(load_start_callback_));
+  }
+
+ private:
+  LoadStartCallback load_start_callback_;
+};
+
+FakeURLLoaderFactoryForBackgroundThread::
+    FakeURLLoaderFactoryForBackgroundThread(
+        LoadStartCallback load_start_callback)
+    : load_start_callback_(std::move(load_start_callback)) {}
+
+FakeURLLoaderFactoryForBackgroundThread::
+    ~FakeURLLoaderFactoryForBackgroundThread() = default;
+
+void FakeURLLoaderFactoryForBackgroundThread::CreateLoaderAndStart(
+    mojo::PendingReceiver<network::mojom::URLLoader> loader,
+    int32_t request_id,
+    uint32_t options,
+    const network::ResourceRequest& request,
+    mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
+  CHECK(load_start_callback_);
+  std::move(load_start_callback_).Run(std::move(loader), std::move(client));
+}
+
+void FakeURLLoaderFactoryForBackgroundThread::Clone(
+    mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
+  // Pass |this| as the receiver context to make sure this object stays alive
+  // while it still has receivers.
+  receivers_.Add(this, std::move(receiver), this);
+}
+
+std::unique_ptr<network::PendingSharedURLLoaderFactory>
+FakeURLLoaderFactoryForBackgroundThread::Clone() {
+  CHECK(load_start_callback_);
+  return std::make_unique<PendingFactory>(std::move(load_start_callback_));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/testing/fake_url_loader_factory_for_background_thread.h b/third_party/blink/renderer/platform/loader/testing/fake_url_loader_factory_for_background_thread.h
new file mode 100644
index 0000000..5a466c6
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/testing/fake_url_loader_factory_for_background_thread.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FAKE_URL_LOADER_FACTORY_FOR_BACKGROUND_THREAD_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FAKE_URL_LOADER_FACTORY_FOR_BACKGROUND_THREAD_H_
+
+#include "base/functional/callback.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/mojom/url_loader.mojom-forward.h"
+
+namespace blink {
+
+// A fake SharedURLLoaderFactory that can handle a fetch request on the
+// background thread. This SharedURLLoaderFactory is cloned and passed to a
+// background thread via PendingFactory.
+// This class can be used for testing the behaviour of a fetch request with a
+// BackgroundURLLoader, or a synchronous fetch request.
+class FakeURLLoaderFactoryForBackgroundThread
+    : public network::SharedURLLoaderFactory {
+ public:
+  using LoadStartCallback = base::OnceCallback<void(
+      mojo::PendingReceiver<network::mojom::URLLoader>,
+      mojo::PendingRemote<network::mojom::URLLoaderClient>)>;
+
+  // `load_start_callback` will be called in a background thread when a fetch
+  // request is started.
+  explicit FakeURLLoaderFactoryForBackgroundThread(
+      LoadStartCallback load_start_callback);
+  FakeURLLoaderFactoryForBackgroundThread(
+      const FakeURLLoaderFactoryForBackgroundThread&) = delete;
+  FakeURLLoaderFactoryForBackgroundThread& operator=(
+      const FakeURLLoaderFactoryForBackgroundThread&) = delete;
+  ~FakeURLLoaderFactoryForBackgroundThread() override;
+
+  // network::SharedURLLoaderFactory:
+  void CreateLoaderAndStart(
+      mojo::PendingReceiver<network::mojom::URLLoader> loader,
+      int32_t request_id,
+      uint32_t options,
+      const network::ResourceRequest& request,
+      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
+      override;
+  void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
+      override;
+  std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override;
+
+ private:
+  class PendingFactory;
+
+  mojo::ReceiverSet<network::mojom::URLLoaderFactory,
+                    scoped_refptr<FakeURLLoaderFactoryForBackgroundThread>>
+      receivers_;
+  LoadStartCallback load_start_callback_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FAKE_URL_LOADER_FACTORY_FOR_BACKGROUND_THREAD_H_
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index af37113d..eb733a3 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -3554,6 +3554,9 @@
       public: true,
     },
     {
+      name: "ServiceWorkerStaticRouterTimingInfo",
+    },
+    {
       name: "SetSequentialFocusStartingPoint",
       status: "experimental",
     },
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index dd74c4c..efe2ee9 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5113,9 +5113,6 @@
 # Sheriff 2021-12-21
 crbug.com/1281792 external/wpt/event-timing/min-duration-threshold.html [ Failure Pass ]
 
-crbug.com/335630145 external/wpt/client-hints/sec-ch-width-auto-sizes-001.https.html [ Failure ]
-crbug.com/335630145 external/wpt/client-hints/sec-ch-width-auto-sizes-003.https.html [ Failure ]
-
 # Sheriff 2021-12-22
 crbug.com/1283295 [ Mac ] fast/text-autosizing/hackernews-comments.html [ Failure Pass ]
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 3e0d215..c314d31 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -2256,7 +2256,7 @@
       "http/tests/inspector-protocol/service-worker/tentative/static-router",
       "http/tests/serviceworker/static-router/"
     ],
-    "args": ["--enable-features=ServiceWorkerStaticRouter,ServiceWorkerStaticRouterNotConditionEnabled",
+    "args": ["--enable-features=ServiceWorkerStaticRouter,ServiceWorkerStaticRouterNotConditionEnabled,ServiceWorkerStaticRouterTimingInfo",
              "--disable-threaded-compositing", "--disable-threaded-animation"],
     "expires": "Jul 14, 2024",
     "owners": ["yyanagisawa@chromium.org", "sisidovski@chromium.org"]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-accumulation-001.html b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-accumulation-001.html
new file mode 100644
index 0000000..08eedbd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/text-box-trim-accumulation-001.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Test choosing the innermost for `text-box-trim` for requested trim metric</title>
+<link rel="help" href="https://drafts.csswg.org/css-inline-3/#propdef-text-box-edge">
+<link rel="help" href="https://drafts.csswg.org/css-inline-3/#propdef-text-box-trim">
+<link rel="match" href="text-box-trim-start-001-ref.html?class=text">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+.spacer {
+  block-size: 100px;
+  background: lightgray;
+}
+.target {
+  font-family: Ahem;
+  font-size: 100px;
+  line-height: 2;
+  text-box-trim: start;
+  text-box-edge: leading;
+}
+.inner {
+  text-box-edge: text;
+}
+</style>
+<div class="spacer"></div>
+<div class="target">
+  <!--
+    When the element that has `text-box-trim` and `.inner` has different
+    `text-box-edge` values, use the innermost one.
+    https://github.com/w3c/csswg-drafts/issues/5426
+  -->
+  <div class="inner">A</div>
+</div>
+<div class="spacer"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ruby/ruby-no-transform-ref.html b/third_party/blink/web_tests/external/wpt/css/css-ruby/ruby-no-transform-ref.html
index 56afd93..1c075de 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ruby/ruby-no-transform-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ruby/ruby-no-transform-ref.html
@@ -8,3 +8,5 @@
   <rbc><rb>a</rb><rb>b</rb></rbc>
   <rtc><rt>A</rt><rt>B</rt></rtc>
 </ruby>
+
+<ruby>a<rt>A</rt>b<rt>B</rt></ruby>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ruby/ruby-no-transform.html b/third_party/blink/web_tests/external/wpt/css/css-ruby/ruby-no-transform.html
index 1cdd4f3..56b2fc8 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ruby/ruby-no-transform.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ruby/ruby-no-transform.html
@@ -15,3 +15,5 @@
   <rbc><rb>a</rb><rb>b</rb></rbc>
   <rtc><rt>A</rt><rt>B</rt></rtc>
 </ruby>
+
+<ruby>a<rt>A</rt>b<rt>B</rt></ruby>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/tentative/static-router/static-router-resource-timing.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/tentative/static-router/static-router-resource-timing.https.html
new file mode 100644
index 0000000..e99c2d3d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/tentative/static-router/static-router-resource-timing.https.html
@@ -0,0 +1,332 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>
+  Static Router: timing information should be shown when used.
+</title>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script src="resources/static-router-helpers.sub.js"></script>
+<body>
+<script>
+const ROUTER_RULE_KEY = 'condition-urlpattern-constructed-source-network';
+const ROUTER_RULE_KEY_URLPATTERN_CACHE =
+  'condition-urlpattern-string-source-cache';
+const ROUTER_RULE_KEY_REQUEST_CACHE = 'condition-request-navigate-source-cache';
+const ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED_MATCH_ALL_CACHE =
+  'condition-urlpattern-constructed-match-all-source-cache';
+const ROUTER_RULE_KEY_REQUEST_FETCH = 'condition-urlpattern-string-source-fetch-event';
+const REGISTERED_ROUTE = 'resources/direct.txt';
+const CACHED_ROUTE = 'resources/cache.txt';
+const NON_REGISTERED_ROUTE = 'resources/simple.html';
+
+const RACE_ROUTER_KEY =
+  'condition-urlpattern-string-source-race-network-and-fetch-handler';
+const RACE_SW_SRC = 'resources/static-router-race-network-and-fetch-handler-sw.js';
+const RACE_ROUTE = 'resources/direct.py';
+
+const host_info = get_host_info();
+const path = new URL(".", window.location).pathname;
+
+function resourceUrl(resource) {
+  return `${host_info['HTTPS_ORIGIN']}${path}${resource}`;
+}
+
+// Verify existance of a PerformanceEntry and the order between the timings of
+// ServiceWorker Static routing API.
+//
+// |options| has these properties:
+// performance: Performance interface to verify existance of the entry.
+// url: the URL of resource
+// description: the description passed to each assertion.
+// matched_source: the expected matched source of router evaluation.
+// actual_source: the expected actual source used to get the resource.
+function test_resource_timing(options) {
+  const description = options.description;
+  const entryList = options.performance.getEntriesByName(resourceUrl(options.url));
+  assert_equals(entryList.length, 1, description);
+  const entry = entryList[0];
+
+  assert_equals(entry.matchedSourceType, options.matched_source_type, description);
+  assert_equals(entry.finalSourceType, options.final_source_type, description);
+
+  assert_greater_than(entry.workerRouterEvaluationStart, 0, description);
+  switch (entry.matchedSouceType) {
+    case 'network':
+      assert_equals(entry.workerStart, 0, description);
+      assert_equals(entry.workerCacheLookupStart, 0, description);
+      assert_less_than_equal(entry.workerRouterEvaluationStart, entry.fetchStart, description);
+      break;
+    case 'cache':
+      assert_equals(entry.workerStart, 0, description);
+      assert_greater_than_equal(entry.workerCacheLookupStart, entry.workerRouterEvaluationStart, description);
+      if (entry.finalSourceType === 'cache') {
+        assert_equals(entry.fetchStart, 0, description);
+        assert_less_than_equal(entry.workerCacheLookupStart, entry.responseStart, description);
+      } else {
+        assert_less_than_equal(entry.workerCacheLookupStart, entry.fetchStart, description);
+      }
+      break;
+    case 'race-network-and-fetch':
+      assert_equals(entry.workerCacheLookupStart, 0, description);
+      if (entry.finalSourceType === 'network') {
+        assert_equals(entry.workerStart, 0, description);
+        assert_less_than_equal(entry.workerRouterEvaluationStart, entry.fetchStart, description);
+      } else {
+        assert_greater_than_equal(entry.workerStart, entry.workerRouterEvaluationStart, description);
+        assert_greater_than_equal(entry.fetchStart, entry.workerStart, description);
+      }
+      break;
+    case 'fetch-event':
+    case '':  // i.e. no matching rules
+      assert_equals(entry.workerCacheLookupStart, 0, description);
+      assert_greater_than_equal(entry.workerStart, entry.workerRouterEvaluationStart, description);
+      assert_greater_than_equal(entry.fetchStart, entry.workerStart, description);
+      break;
+  }
+}
+
+promise_test(async t => {
+  const worker = await registerAndActivate(t, ROUTER_RULE_KEY_REQUEST_FETCH);
+  const rnd = randomString();
+  const url = `${NON_REGISTERED_ROUTE}?nonce=${rnd}`;
+  const iframe = await createIframe(t, url);
+  const {errors, requests} = await get_info_from_worker(worker);
+
+  assert_equals(errors.length, 0);
+  assert_equals(requests.length, 1);
+  assert_equals(iframe.contentWindow.document.body.innerText, rnd);
+
+  test_resource_timing({
+    performance: iframe.contentWindow.performance,
+    url: url,
+    matched_source_type: 'fetch-event',
+    final_source_type: 'fetch-event',
+    description: "fetch-event as source on main resource"
+  });
+}, 'Main resource matched the rule with fetch-event source');
+
+iframeTest(REGISTERED_ROUTE, ROUTER_RULE_KEY, async (t, iwin, worker) => {
+  const {requests} = await get_info_from_worker(worker);
+  assert_equals(requests.length, 0);
+  assert_equals(iwin.document.body.innerText, "Network\n");
+  test_resource_timing({
+    performance: iwin.performance,
+    url: REGISTERED_ROUTE,
+    matched_source_type: 'network',
+    final_source_type: 'network',
+    description: "network as source on main resource"
+  });
+}, 'Main resource load matched with the condition and resource timing');
+
+iframeTest(NON_REGISTERED_ROUTE, ROUTER_RULE_KEY, async (t, iwin, worker) => {
+  const {requests} = await get_info_from_worker(worker);
+  assert_equals(requests.length, 1);
+  assert_equals(
+    requests[0].url,
+    resourceUrl(NON_REGISTERED_ROUTE));
+  assert_equals(requests[0].mode, 'navigate');
+  test_resource_timing({
+    performance: iwin.performance,
+    url: NON_REGISTERED_ROUTE,
+    matched_source_type: '',
+    final_source_type: '',
+    description: "no rule matched on main resource"
+  });
+}, 'Main resource load not matched with the condition and resource timing');
+
+iframeTest(CACHED_ROUTE, ROUTER_RULE_KEY_URLPATTERN_CACHE, async (t, iwin, worker) => {
+  const {requests} = await get_info_from_worker(worker);
+  assert_equals(requests.length, 0);
+  assert_equals(iwin.document.body.innerText, "From cache");
+  test_resource_timing({
+    performance: iwin.performance,
+    url: CACHED_ROUTE,
+    matched_source_type: 'cache',
+    final_source_type: 'cache',
+    description: "cache as source on main resource and cache hit"
+  });
+}, 'Main resource load matched with the cache source and resource timing');
+
+iframeTest(NON_REGISTERED_ROUTE, ROUTER_RULE_KEY_REQUEST_CACHE, async (t, iwin, worker) => {
+  const {requests} = await get_info_from_worker(worker);
+  // When the request matched to the rule with the "cache" source but failed to
+  // get the cache entry, the fetch handler is not involved and the network
+  // fallback is triggered instead.
+  assert_equals(requests.length, 0);
+  assert_equals(iwin.document.body.innerText, "Here's a simple html file.");
+  test_resource_timing({
+    performance: iwin.performance,
+    url: NON_REGISTERED_ROUTE,
+    matched_source_type: 'cache',
+    final_source_type: 'network',
+    description: "cache as source on main resource and cache miss, fallback to network"
+  });
+}, 'Main resource fallback to the network when there is no cache entry and resource timing');
+
+// Subresource
+iframeTest(NON_REGISTERED_ROUTE, ROUTER_RULE_KEY_REQUEST_FETCH, async (t, iwin, worker) => {
+  const rnd = randomString();
+  const subresource = `?nonce=${rnd}`;
+  const response = await iwin.fetch(subresource);
+
+  assert_equals(response.status, 200);
+  assert_equals(await response.text(), rnd);
+  const {requests} = await get_info_from_worker(worker);
+  // Main resource request + subreosurce request = 2.
+  assert_equals(requests.length, 2);
+
+  test_resource_timing({
+    performance: iwin.performance,
+    url: `${NON_REGISTERED_ROUTE}${subresource}`,
+    matched_source_type: 'fetch-event',
+    final_source_type: 'fetch-event',
+    description: "fetch-event as source on sub resource"
+  });
+}, 'Subresource load matched the rule fetch-event source');
+
+iframeTest(NON_REGISTERED_ROUTE, ROUTER_RULE_KEY, async (t, iwin) => {
+  const rnd = randomString();
+  const subresource = `?nonce=${rnd}`;
+  const response = await iwin.fetch(subresource);
+  assert_equals(await response.text(), rnd);
+  test_resource_timing({
+    performance: iwin.performance,
+    url: NON_REGISTERED_ROUTE + subresource,
+    matched_source_type: '',
+    final_source_type: '',
+    description: "no source type matched"
+  });
+}, 'Subresource load not matched with URLPattern condition');
+
+iframeTest(REGISTERED_ROUTE, ROUTER_RULE_KEY, async (t, iwin) => {
+  const rnd = randomString();
+  const subresource = `?nonce=${rnd}`;
+  const response = await iwin.fetch(subresource);
+  assert_equals(await response.text(), "Network\n");
+  test_resource_timing({
+    performance: iwin.performance,
+    url: REGISTERED_ROUTE + subresource,
+    matched_source_type: 'network',
+    final_source_type: 'network',
+    description: "network as source on subresource"
+  });
+}, 'Subresource load matched with URLPattern condition');
+
+iframeTest(NON_REGISTERED_ROUTE, ROUTER_RULE_KEY_URLPATTERN_CACHE, async (t, iwin) => {
+  // No need to set `resources/` because the request is dispatched from iframe.
+  const CACHED_FILE = 'cache.txt';
+  const response = await iwin.fetch(CACHED_FILE);
+  assert_equals(await response.text(), "From cache");
+  test_resource_timing({
+    performance: iwin.performance,
+    url: CACHED_ROUTE, // We need a path including `resources/` to get the resource
+    matched_source_type: 'cache',
+    final_source_type: 'cache',
+    description: "cache as source on subresource and cache hits"
+  });
+}, 'Subresource load matched with the cache source rule');
+
+iframeTest(REGISTERED_ROUTE, ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED_MATCH_ALL_CACHE, async (t, iwin, worker) => {
+  // Send a request, which is not stored in the cache, but it exists over the network.
+  const rnd = randomString();
+  let subresource = `?nonce=${rnd}`;
+  let response = await iwin.fetch(subresource);
+  assert_equals(await response.text(), "Network\n");
+  assert_equals(response.status, 200);
+
+  // Request is not handled by ServiceWorker.
+  const {requests} = await get_info_from_worker(worker);
+  assert_equals(requests.length, 0);
+  test_resource_timing({
+    performance: iwin.performance,
+    url: `${REGISTERED_ROUTE}${subresource}`,
+    matched_source_type: 'cache',
+    final_source_type: 'network',
+    description: "cache as source on subresource and cache misses"
+  });
+}, 'Subresource load did not match with the cache and fallback to the network');
+
+// Race Tests
+promise_test(async t => {
+  const rnd = randomString();
+  const url = `${RACE_ROUTE}?nonce=${rnd}&server_slow`;
+  const worker = await registerAndActivate(t, RACE_ROUTER_KEY, RACE_SW_SRC);
+  const iframe = await createIframe(t, url);
+  // Expect the response from the fetch handler.
+  assert_equals(iframe.contentWindow.document.body.innerText, rnd);
+  const {requests}  = await get_info_from_worker(worker);
+  assert_equals(requests.length, 1);
+  test_resource_timing({
+    performance: iframe.contentWindow.performance,
+    url: url,
+    matched_source_type: 'race-network-and-fetch',
+    final_source_type: 'fetch-event',
+    description: "race as source on main resource, and fetch-event wins"
+  });
+}, 'Main resource load matched the rule with race-network-and-fetch-handler source, and the fetch handler response is faster than the server response');
+
+promise_test(async t => {
+  const rnd = randomString();
+  const url = `${RACE_ROUTE}?nonce=${rnd}&sw_slow`;
+  const worker = await registerAndActivate(t, RACE_ROUTER_KEY, RACE_SW_SRC);
+  const iframe = await createIframe(t, url);
+  // Expect the response from the netowrk request.
+  assert_equals(iframe.contentWindow.document.body.innerText, "Network with GET request");
+  // Ensure the fetch handler is also executed.
+  const {requests}  = await get_info_from_worker(worker);
+  assert_equals(requests.length, 1);
+  test_resource_timing({
+    performance: iframe.contentWindow.performance,
+    url: url,
+    matched_source_type: 'race-network-and-fetch',
+    final_source_type: 'network',
+    description: "race as source on main resource, and network wins"
+  });
+}, 'Main resource load matched the rule with race-network-and-fetch-handler source, and the server reseponse is faster than the fetch handler');
+
+promise_test(async t => {
+  const rnd = randomString();
+  const worker = await registerAndActivate(t, RACE_ROUTER_KEY, RACE_SW_SRC);
+  const iframe = await createIframe(t, RACE_ROUTE);
+  const subresource = `?nonce=${rnd}&server_slow`;
+  // Expect the response from the fetch handler.
+  const response = await iframe.contentWindow.fetch(subresource);
+  assert_equals(response.status, 200);
+  assert_equals(await response.text(), rnd);
+  const {requests}  = await get_info_from_worker(worker);
+  assert_equals(requests.length, 2);
+
+  test_resource_timing({
+    performance: iframe.contentWindow.performance,
+    url: `${RACE_ROUTE}${subresource}`,
+    matched_source_type: 'race-network-and-fetch',
+    final_source_type: 'fetch-event',
+    description: "race as source on subresource and fetch wins"
+  });
+}, 'Subresource load matched the rule with race-network-and-fetch-handler source, and the fetch handler response is faster than the server response');
+
+promise_test(async t => {
+  const rnd = randomString();
+  const worker = await registerAndActivate(t, RACE_ROUTER_KEY, RACE_SW_SRC);
+  const iframe = await createIframe(t, RACE_ROUTE);
+  const subresource = `?nonce=${rnd}&sw_slow`;
+  // Expect the response from the network request.
+  const response = await iframe.contentWindow.fetch(subresource);
+  assert_equals(response.status, 200);
+  assert_equals(await response.text(), "Network with GET request");
+  // Ensure the fetch handler is also executed.
+  const {requests}  = await get_info_from_worker(worker);
+  assert_equals(requests.length, 2);
+
+  test_resource_timing({
+    performance: iframe.contentWindow.performance,
+    url: `${RACE_ROUTE}${subresource}`,
+    matched_source_type: 'race-network-and-fetch',
+    final_source_type: 'network',
+    description: "race as source on subresource and network wins"
+  });
+}, 'Subresource load matched the rule with race-network-and-fetch-handler source, and the server reseponse is faster than the fetch handler');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/elementwise-logical.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/elementwise-logical.https.any.js
index 86183b07..25d7b59 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/elementwise-logical.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/elementwise-logical.https.any.js
@@ -17,4 +17,4 @@
 });
 
 // The `not()` operator is unary.
-validateInputFromOtherBuilder('not');
+validateInputFromAnotherBuilder('not');
diff --git a/third_party/blink/web_tests/platform/linux/virtual/webnn-service-on-cpu/external/wpt/webnn/validation_tests/elementwise-logical.https.any-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/webnn-service-on-cpu/external/wpt/webnn/validation_tests/elementwise-logical.https.any-expected.txt
deleted file mode 100644
index 109e625..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/webnn-service-on-cpu/external/wpt/webnn/validation_tests/elementwise-logical.https.any-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: validateInputFromOtherBuilder is not defined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/linux/virtual/webnn-service-on-cpu/external/wpt/webnn/validation_tests/elementwise-logical.https.any.worker-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/webnn-service-on-cpu/external/wpt/webnn/validation_tests/elementwise-logical.https.any.worker-expected.txt
deleted file mode 100644
index 1173f57..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/webnn-service-on-cpu/external/wpt/webnn/validation_tests/elementwise-logical.https.any.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Error in remote https://web-platform.test:8444/webnn/validation_tests/elementwise-logical.https.any.js: Uncaught ReferenceError: validateInputFromOtherBuilder is not defined
-Harness: the test ran to completion.
-
diff --git a/third_party/chromium-variations b/third_party/chromium-variations
index a243710..9e9b012 160000
--- a/third_party/chromium-variations
+++ b/third_party/chromium-variations
@@ -1 +1 @@
-Subproject commit a24371073c455dfc595f7113e776eb8c0234d438
+Subproject commit 9e9b01285b3b1febec28c95040e7ed89e3b86bc8
diff --git a/third_party/depot_tools b/third_party/depot_tools
index 6708d95..d32e1cb 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit 6708d95ec6d819098093cd57c746636a5bb9a4ea
+Subproject commit d32e1cb5717853a1837347884abc85149813c398
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index dc81725..d680c3e 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit dc817257b8d98a65a5cdafd557b1ee955c19de20
+Subproject commit d680c3ebbbb765c508de563623aa67943bed945c
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index cf44aab..bd996cb 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit cf44aab1d7aea14be5623377a80218842e3c5b17
+Subproject commit bd996cb88ce5278a2a7ebfb999792c0ad8eda3ae
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 5b30bac3..1e9b8ca 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -29,6 +29,7 @@
       'chromeos-arm-generic-cfi-thin-lto-chrome': 'chromeos_arm-generic_cfi_thin_lto_official_reclient',
       'chromeos-arm64-generic-cfi-thin-lto-chrome': 'chromeos_arm64-generic_cfi_thin_lto_official_reclient',
       'chromeos-betty-arc-r-chrome': 'chromeos_betty-arc-r_include_unwind_tables_official_use_fake_dbus_clients_reclient',
+      'chromeos-betty-chrome': 'chromeos_betty_include_unwind_tables_official_reclient',
       'chromeos-betty-pi-arc-cfi-thin-lto-chrome': 'chromeos_betty-pi-arc_cfi_thin_lto_official_reclient',
       'chromeos-betty-pi-arc-chrome': 'chromeos_betty-pi-arc_dchecks_reclient',
       'chromeos-brya-chrome-skylab': 'chromeos_brya_include_unwind_tables_official_skylab_reclient',
@@ -212,6 +213,7 @@
     },
 
     'internal.chromeos.fyi': {
+      'chromeos-betty-chrome': 'chromeos_betty-arc-r_include_unwind_tables_official_use_fake_dbus_clients_reclient',
       'chromeos-betty-pi-arc-chrome-accessibility-fyi': 'chromeos_betty-pi-arc_dchecks_reclient',
       'chromeos-betty-pi-arc-chrome-dchecks': 'chromeos_betty-pi-arc_dchecks_reclient',
       'chromeos-betty-pi-arc-finch-smoke-chrome': 'chromeos_betty-pi-arc_dchecks_reclient',
@@ -468,6 +470,11 @@
       'also_build_lacros_chrome_for_architecture_amd64', 'ozone_headless'
     ],
 
+    'chromeos_betty_include_unwind_tables_official_reclient': [
+      'chromeos_device_reclient', 'betty', 'include_unwind_tables', 'official',
+      'also_build_lacros_chrome_for_architecture_amd64', 'ozone_headless'
+    ],
+
     'chromeos_brya_include_unwind_tables_official_dchecks_skylab_reclient': [
       'chromeos_device_reclient', 'brya', 'include_unwind_tables', 'official', 'dcheck_always_on', 'is_skylab', 'ozone_headless',
       'also_build_lacros_chrome_for_architecture_amd64',
@@ -976,6 +983,10 @@
       'gn_args': 'is_asan=true',
     },
 
+    'betty': {
+      'args_file': '//build/args/chromeos/betty.gni',
+    },
+
     'betty-arc-r': {
       'args_file': '//build/args/chromeos/betty-arc-r.gni',
     },
diff --git a/tools/mb/mb_config_expectations/chrome.json b/tools/mb/mb_config_expectations/chrome.json
index 79972492..e7e6bc2 100644
--- a/tools/mb/mb_config_expectations/chrome.json
+++ b/tools/mb/mb_config_expectations/chrome.json
@@ -43,6 +43,19 @@
       "use_remoteexec": true
     }
   },
+  "chromeos-betty-chrome": {
+    "args_file": "//build/args/chromeos/betty.gni",
+    "gn_args": {
+      "also_build_lacros_chrome_for_architecture": "amd64",
+      "dcheck_always_on": false,
+      "exclude_unwind_tables": false,
+      "is_chrome_branded": true,
+      "is_chromeos_device": true,
+      "is_official_build": true,
+      "ozone_platform_headless": true,
+      "use_remoteexec": true
+    }
+  },
   "chromeos-betty-pi-arc-cfi-thin-lto-chrome": {
     "args_file": "//build/args/chromeos/betty-pi-arc.gni",
     "gn_args": {
diff --git a/tools/mb/mb_config_expectations/internal.chromeos.fyi.json b/tools/mb/mb_config_expectations/internal.chromeos.fyi.json
index fe4e64d..428b586b 100644
--- a/tools/mb/mb_config_expectations/internal.chromeos.fyi.json
+++ b/tools/mb/mb_config_expectations/internal.chromeos.fyi.json
@@ -1,4 +1,18 @@
 {
+  "chromeos-betty-chrome": {
+    "args_file": "//build/args/chromeos/betty-arc-r.gni",
+    "gn_args": {
+      "also_build_lacros_chrome_for_architecture": "amd64",
+      "dcheck_always_on": false,
+      "exclude_unwind_tables": false,
+      "is_chrome_branded": true,
+      "is_chromeos_device": true,
+      "is_official_build": true,
+      "ozone_platform_headless": true,
+      "use_real_dbus_clients": false,
+      "use_remoteexec": true
+    }
+  },
   "chromeos-betty-pi-arc-chrome-accessibility-fyi": {
     "args_file": "//build/args/chromeos/betty-pi-arc.gni",
     "gn_args": {
diff --git a/tools/metrics/histograms/metadata/file/enums.xml b/tools/metrics/histograms/metadata/file/enums.xml
index 475131e..b9f62ae 100644
--- a/tools/metrics/histograms/metadata/file/enums.xml
+++ b/tools/metrics/histograms/metadata/file/enums.xml
@@ -836,6 +836,7 @@
   <int value="18" label="Fallback (QuickOffice) after open attempt"/>
   <int value="19" label="Cancelled at fallback after open attempt"/>
   <int value="20" label="Cannot get fallback choice after open attempt"/>
+  <int value="21" label="File already being opened"/>
 </enum>
 
 <enum name="RestoreFailedNoParentType">
diff --git a/ui/views/accessibility/view_accessibility.cc b/ui/views/accessibility/view_accessibility.cc
index f46181bf..68bfd70 100644
--- a/ui/views/accessibility/view_accessibility.cc
+++ b/ui/views/accessibility/view_accessibility.cc
@@ -52,10 +52,6 @@
 
 }  // namespace
 
-#define RETURN_IF_UNAVAILABLE() \
-  if (is_widget_closed_)        \
-    return;
-
 #if !BUILDFLAG_INTERNAL_HAS_NATIVE_ACCESSIBILITY()
 // static
 std::unique_ptr<ViewAccessibility> ViewAccessibility::Create(View* view) {
@@ -144,13 +140,33 @@
   data->AddStringAttribute(ax::mojom::StringAttribute::kClassName,
                            view_->GetClassName());
 
-  if (is_widget_closed_) {
-    // TODO(javiercon): Eventually, we should remove this call and just return.
-    // Must keep here for now in case someone queries this function right after
-    // the widget is closed.
-    // We need to return before the merge, otherwise the call
-    // to View::GetAccessibleNodeData might crash.
-    SetDataForClosedWidget(data);
+  // Views may misbehave if their widget is closed; return an unknown role
+  // rather than possibly crashing.
+  const views::Widget* widget = view_->GetWidget();
+  if (!ignore_missing_widget_for_testing_ &&
+      (!widget || !widget->widget_delegate() || widget->IsClosed())) {
+    data->role = ax::mojom::Role::kUnknown;
+    data->SetRestriction(ax::mojom::Restriction::kDisabled);
+
+    // TODO(accessibility): Returning early means that any custom data which
+    // had been set via the Override functions is not included. Preserving
+    // and exposing these properties might be worth doing, even in the case
+    // of object destruction.
+
+    // Ordinarily, a view cannot be focusable if its widget has already closed.
+    // So, it would have been appropriate to set the focusable state to false in
+    // this particular case. However, the `FocusManager` may sometimes try to
+    // retrieve the focusable state of this view via
+    // `View::IsAccessibilityFocusable()`, even after this view's widget has
+    // been closed. Returning the wrong result might cause a crash, because the
+    // focus manager might be expecting the result to be the same regardless of
+    // the state of the view's widget.
+    if (ViewAccessibility::IsAccessibilityFocusable()) {
+      data->AddState(ax::mojom::State::kFocusable);
+      // Set this node as intentionally nameless to avoid DCHECKs for a missing
+      // name of a focusable.
+      data->SetNameExplicitlyEmpty();
+    }
     return;
   }
 
@@ -175,7 +191,6 @@
   if (child_tree_id_) {
     data->AddChildTreeId(child_tree_id_.value());
 
-    const views::Widget* widget = view_->GetWidget();
     if (widget && widget->GetNativeView() && display::Screen::GetScreen()) {
       const float scale_factor =
           display::Screen::GetScreen()
@@ -380,7 +395,6 @@
 }
 
 void ViewAccessibility::SetRole(const ax::mojom::Role role) {
-  RETURN_IF_UNAVAILABLE();
   DCHECK(IsValidRoleForViews(role)) << "Invalid role for Views.";
   if (role == GetCachedRole()) {
     return;
@@ -393,7 +407,6 @@
 
 void ViewAccessibility::SetRole(const ax::mojom::Role role,
                                 const std::u16string& role_description) {
-  RETURN_IF_UNAVAILABLE();
   if (role_description == data_.GetString16Attribute(
                               ax::mojom::StringAttribute::kRoleDescription)) {
     // No changes to the role description, update the role and return early.
@@ -413,7 +426,6 @@
 
 void ViewAccessibility::SetName(const std::string& name,
                                 ax::mojom::NameFrom name_from) {
-  RETURN_IF_UNAVAILABLE();
   DCHECK_NE(name_from, ax::mojom::NameFrom::kNone);
   // Ensure we have a current `name_from` value. For instance, the name might
   // still be an empty string, but a view is now indicating that this is by
@@ -537,7 +549,6 @@
 }
 
 void ViewAccessibility::SetIsEnabled(bool is_enabled) {
-  RETURN_IF_UNAVAILABLE();
   if (is_enabled == GetIsEnabled()) {
     return;
   }
@@ -795,13 +806,6 @@
   accessibility_events_callback_ = std::move(callback);
 }
 
-void ViewAccessibility::OnWidgetClosing(Widget* widget) {
-  // The RootView's ViewAccessibility should be the only registered
-  // WidgetObserver.
-  DCHECK_EQ(view_, widget->GetRootView());
-  OnWidgetClosingRecursive();
-}
-
 void ViewAccessibility::PruneSubtree() {
   internal::ScopedChildrenLock lock(view_);
   for (auto& child : view_->children()) {
@@ -841,36 +845,4 @@
   UpdateFocusableState();
 }
 
-void ViewAccessibility::OnWidgetClosingRecursive() {
-  ui::AXNodeData data;
-  SetDataForClosedWidget(&data);
-  data_ = data;
-
-  is_widget_closed_ = true;
-
-  internal::ScopedChildrenLock lock(view_);
-  for (auto& child : view_->children()) {
-    child->GetViewAccessibility().OnWidgetClosingRecursive();
-  }
-}
-
-void ViewAccessibility::SetDataForClosedWidget(ui::AXNodeData* data) const {
-  data->role = ax::mojom::Role::kUnknown;
-  data->SetRestriction(ax::mojom::Restriction::kDisabled);
-  // Ordinarily, a view cannot be focusable if its widget has already closed.
-  // So, it would have been appropriate to set the focusable state to false in
-  // this particular case. However, the `FocusManager` may sometimes try to
-  // retrieve the focusable state of this view via
-  // `View::IsAccessibilityFocusable()`, even after this view's widget has
-  // been closed. Returning the wrong result might cause a crash, because the
-  // focus manager might be expecting the result to be the same regardless of
-  // the state of the view's widget.
-  if (ViewAccessibility::IsAccessibilityFocusable()) {
-    data->AddState(ax::mojom::State::kFocusable);
-    // Set this node as intentionally nameless to avoid DCHECKs for a missing
-    // name of a focusable.
-    data->SetNameExplicitlyEmpty();
-  }
-}
-
 }  // namespace views
diff --git a/ui/views/accessibility/view_accessibility.h b/ui/views/accessibility/view_accessibility.h
index 17377c5e..499113f 100644
--- a/ui/views/accessibility/view_accessibility.h
+++ b/ui/views/accessibility/view_accessibility.h
@@ -21,7 +21,6 @@
 #include "ui/views/accessibility/ax_virtual_view.h"
 #include "ui/views/accessibility/view_accessibility_utils.h"
 #include "ui/views/views_export.h"
-#include "ui/views/widget/widget_observer.h"
 
 namespace ui {
 
@@ -44,7 +43,7 @@
 //
 // In most cases, subclasses of |ViewAccessibility| own the |AXPlatformNode|
 // that implements the native accessibility APIs on a specific platform.
-class VIEWS_EXPORT ViewAccessibility : public WidgetObserver {
+class VIEWS_EXPORT ViewAccessibility {
  public:
   using AccessibilityEventsCallback =
       base::RepeatingCallback<void(const ui::AXPlatformNodeDelegate*,
@@ -55,7 +54,7 @@
 
   ViewAccessibility(const ViewAccessibility&) = delete;
   ViewAccessibility& operator=(const ViewAccessibility&) = delete;
-  ~ViewAccessibility() override;
+  virtual ~ViewAccessibility();
 
   // Modifies |node_data| to reflect the current accessible state of the
   // associated View, taking any custom overrides into account
@@ -401,11 +400,6 @@
   const AccessibilityEventsCallback& accessibility_events_callback() const;
   void set_accessibility_events_callback(AccessibilityEventsCallback callback);
 
-  // Widget Observer
-  // Views may misbehave if their widget is closed; set "null-like" attributes
-  // rather than possibly crashing.
-  void OnWidgetClosing(Widget* widget) override;
-
  protected:
   explicit ViewAccessibility(View* view);
 
@@ -428,10 +422,6 @@
   // is 'kNone'.
   void UpdateIgnoredState();
 
-  void OnWidgetClosingRecursive();
-
-  void SetDataForClosedWidget(ui::AXNodeData* data) const;
-
   // Weak. Owns this.
   const raw_ptr<View> view_;
 
@@ -495,10 +485,6 @@
   bool pause_accessibility_events_ = false;
 
   bool ignore_missing_widget_for_testing_ = false;
-
-  bool is_widget_closed_ = false;
-
-  base::ScopedObservation<Widget, WidgetObserver> observation_{this};
 };
 
 class IgnoreMissingWidgetForTestingScopedSetter {
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate.cc b/ui/views/accessibility/view_ax_platform_node_delegate.cc
index 65a39c3..db4005f 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate.cc
@@ -315,15 +315,6 @@
 
   // Clear the data, then populate it.
   data_ = ui::AXNodeData();
-
-  if (!view()->GetWidget()) {
-    // This is to be consistent with what Views expect and what is being done in
-    // ViewAccessibility::GetAccessibleNodeData if the widget is null.
-    data_.role = ax::mojom::Role::kUnknown;
-    data_.SetRestriction(ax::mojom::Restriction::kDisabled);
-    return data_;
-  }
-
   GetAccessibleNodeData(&data_);
 
   // View::IsDrawn is true if a View is visible and all of its ancestors are
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 405d94f..423a2bd 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -3799,8 +3799,7 @@
   // unfocusable. If the view is still focusable or is not focused, we can
   // return early avoiding further unnecessary checks. Focusability check is
   // performed first as it tends to be faster.
-  if (GetViewAccessibility().ViewAccessibility::IsAccessibilityFocusable() ||
-      !HasFocus()) {
+  if (GetViewAccessibility().IsAccessibilityFocusable() || !HasFocus()) {
     return;
   }
 
diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc
index 04f83aa..4f7f2ce7 100644
--- a/ui/views/widget/root_view.cc
+++ b/ui/views/widget/root_view.cc
@@ -266,17 +266,9 @@
   AddPostTargetHandler(post_dispatch_handler_.get());
   SetEventTargeter(
       std::unique_ptr<ViewTargeter>(new RootViewTargeter(this, this)));
-
-  // We need to add the RootView's ViewAccessibility as an observer of the
-  // widget, so that when the widget is closed, the accessible data is set
-  // accordingly.
-  widget->AddObserver(&GetViewAccessibility());
 }
 
 RootView::~RootView() {
-  if (GetWidget()) {
-    GetWidget()->RemoveObserver(&GetViewAccessibility());
-  }
   // If we have children remove them explicitly so to make sure a remove
   // notification is sent for each one of them.
   RemoveAllChildViews();
diff --git a/url/url_constants.h b/url/url_constants.h
index fe4390e..57dd387 100644
--- a/url/url_constants.h
+++ b/url/url_constants.h
@@ -25,6 +25,8 @@
 inline constexpr char16_t kAndroidScheme16[] = u"android";
 inline constexpr char kBlobScheme[] = "blob";
 inline constexpr char16_t kBlobScheme16[] = u"blob";
+inline constexpr char kChromiumXCallback[] = "chromium-x-callback";
+inline constexpr char16_t kChromiumXCallback16[] = u"chromium-x-callback";
 inline constexpr char kContentScheme[] = "content";
 inline constexpr char16_t kContentScheme16[] = u"content";
 inline constexpr char kContentIDScheme[] = "cid";
diff --git a/url/url_util.cc b/url/url_util.cc
index 3191f87..bf08f74 100644
--- a/url/url_util.cc
+++ b/url/url_util.cc
@@ -121,6 +121,8 @@
   std::vector<std::string> opaque_non_special_schemes = {
       // See https://crrev.com/c/5465607 for the reason.
       kAndroidScheme,
+      // Temporarily opted-out. See https://crrev.com/c/5539140.
+      kChromiumXCallback,
   };
 
   // Schemes with a predefined default custom handler.