diff --git a/DEPS b/DEPS
index 195044b..7b0821f 100644
--- a/DEPS
+++ b/DEPS
@@ -178,11 +178,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '4e9cfe7691dda3ca829e4cf7fef49a25f0c4f3f2',
+  'skia_revision': '00ddb0029da729c4cb456d5fe39f4a65c8531ca2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '13387c560ef69e6c40088f04b9397f5e765766ee',
+  'v8_revision': 'd58488c28be7c9115cd4e4ed2d752102f11c952d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -190,7 +190,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '9823d86a5a400418277777bad7d78006a4cd2ef1',
+  'angle_revision': '3d598abb3014f0e10a4565a4fb01556e4152ffef',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -198,7 +198,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '3ea38438da69868f33aac23ce7d1b3575479fb0a',
+  'pdfium_revision': '2acdf79f9eef6c65ea4e2f546454c4c562d45588',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -229,7 +229,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '093f87bfe45160195ade7bd5174bbaaf50ebd6be',
+  'freetype_revision': '3f70e6d20c82b28174096adcd0657b3c998b007b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
@@ -241,7 +241,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'ed728376c1a8e8b8a689cd22d7fe953a634c9d5e',
+  'catapult_revision': 'be5a62acfa44dd850da59380af898ffc64483a93',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -249,7 +249,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': '568858e7437c6cfbd54efb492a5c31685c7822c3',
+  'devtools_frontend_revision': '32b1b68d990ee2c1ceb41109ae19a4a2354e4a2b',
   # 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.
@@ -293,7 +293,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spirv_cross_revision': '7e0295abf81cc939ecb2644c88592d77407d18d3',
+  'spirv_cross_revision': 'd638d2df9c8c4a862e0af829cf49cc6dcbb235a2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -524,7 +524,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '459066d00ee638e32b6c46ed05fa7242ed524ae3',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '996ef4e29110ef6da9a34771687235a965c18072',
       'condition': 'checkout_ios',
   },
 
@@ -927,7 +927,7 @@
     Var('chromium_git') + '/codecs/libgav1.git' + '@' + '638ef84819f8b3cd614dcf63378fe4814aa4cb2a',
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '55f9d97f3b1e2b9d0a5f015a3075512f698e1efa',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '4fa68edd68197a8c77779942b5d973f89c621752',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1220,7 +1220,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '237c0fa4e1f0ff06ab4573cb9010e746fb32b1bd',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd4e0aadfce0df20f5b006c952205e71e9cfe4dde',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1298,7 +1298,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': '5LzaFiFYMxwWXcgus5JjF74yr90M5oz9IMo29pTdoLgC'
+              'version': 'k9-kEftX16LlRF3TNODWTs4uqKfXXrwIK3wkb2CNnbsC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1453,7 +1453,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '1c330752579e584561fc1cb88cc76d1905d22375',
+    Var('webrtc_git') + '/src.git' + '@' + 'b0bd0708d6542ce837d0499975fab7b22d2fad20',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1525,7 +1525,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e76c18d893de41a79c30318984af1df7deffb43f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@568a908f7b188abb0a47e59e101e701df6f780df',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 2393b51..856b07f 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -448,9 +448,11 @@
   return rb.GetImageNamed(IDR_DEFAULT_FAVICON).AsImageSkia();
 }
 
-bool AwContentBrowserClient::AllowAppCache(const GURL& manifest_url,
-                                           const GURL& first_party,
-                                           content::BrowserContext* context) {
+bool AwContentBrowserClient::AllowAppCache(
+    const GURL& manifest_url,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& top_frame_origin,
+    content::BrowserContext* context) {
   // WebView doesn't have a per-site policy for locally stored data,
   // instead AppCache can be disabled for individual WebViews.
   return true;
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index 32d5be9b..fb6dc56 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -78,7 +78,8 @@
   std::string GetAcceptLangs(content::BrowserContext* context) override;
   gfx::ImageSkia GetDefaultFavicon() override;
   bool AllowAppCache(const GURL& manifest_url,
-                     const GURL& first_party,
+                     const GURL& site_for_cookies,
+                     const base::Optional<url::Origin>& top_frame_origin,
                      content::BrowserContext* context) override;
   scoped_refptr<content::QuotaPermissionContext> CreateQuotaPermissionContext()
       override;
diff --git a/ash/public/cpp/login_types.h b/ash/public/cpp/login_types.h
index b8b71255..67dda195 100644
--- a/ash/public/cpp/login_types.h
+++ b/ash/public/cpp/login_types.h
@@ -273,7 +273,7 @@
 
   // True if the display password button should be visible on the login/lock
   // screen for this user.
-  bool show_display_password_button = true;
+  bool show_display_password_button = false;
 
   // Contains the public account information if user type is PUBLIC_ACCOUNT.
   base::Optional<PublicAccountInfo> public_account_info;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 3a3091a..6c36c61 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2972,7 +2972,8 @@
     if (current_cpu == "arm") {
       sources += [ "profiler/chrome_unwinder_android_unittest.cc" ]
     }
-    if (current_cpu == "arm" || current_cpu == "arm64") {
+    if (!exclude_unwind_tables &&
+        (current_cpu == "arm" || current_cpu == "arm64")) {
       sources += [ "profiler/native_unwinder_android_unittest.cc" ]
       include_dirs =
           [ "//third_party/libunwindstack/src/libunwindstack/include" ]
diff --git a/base/threading/hang_watcher_unittest.cc b/base/threading/hang_watcher_unittest.cc
index 15d43763..4a6033f 100644
--- a/base/threading/hang_watcher_unittest.cc
+++ b/base/threading/hang_watcher_unittest.cc
@@ -486,7 +486,8 @@
 
 // If the HangWatcher detects it slept for longer than expected it will not
 // monitor.
-TEST_F(HangWatcherPeriodicMonitoringTest, NoMonitorOnOverSleep) {
+// TODO(crbug.com/1081654): Test flaky on ChromeOS.
+TEST_F(HangWatcherPeriodicMonitoringTest, DISABLED_NoMonitorOnOverSleep) {
   RunLoop run_loop;
 
   // If a call to HangWatcher::Monitor() takes place the test will instantly
diff --git a/build/config/c++/libc++.natvis b/build/config/c++/libc++.natvis
index 2603e2e8..9a49a29 100644
--- a/build/config/c++/libc++.natvis
+++ b/build/config/c++/libc++.natvis
@@ -248,6 +248,13 @@
     </Expand>
   </Type>
 
+  <Type Name="std::__1::priority_queue&lt;*&gt;">
+    <DisplayString>{c}</DisplayString>
+    <Expand>
+      <ExpandedItem>c</ExpandedItem>
+      <Item Name="[comp]">comp</Item>
+    </Expand>
+  </Type>
 
   <Type Name="std::__1::set&lt;*&gt;">
     <Intrinsic Name="size" Expression="*(size_type*)&amp;__tree_.__pair3_" />
@@ -283,11 +290,81 @@
     </Expand>
   </Type>
 
-  <Type Name="std::__1::priority_queue&lt;*&gt;">
-    <DisplayString>{c}</DisplayString>
+  <Type Name="std::__1::__tuple_leaf&lt;*,*,0&gt;">
+    <DisplayString>{__value_}</DisplayString>
+  </Type>
+
+  <Type Name="std::__1::tuple&lt;&gt;">
+    <DisplayString>()</DisplayString>
+  </Type>
+
+  <Type Name="std::__1::tuple&lt;*&gt;">
+    <DisplayString>({(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_})</DisplayString>
+      <Expand>
+          <Item Name="[0]">(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_</Item>
+      </Expand>
+  </Type>
+
+  <Type Name="std::__1::tuple&lt;*,*&gt;">
+    <DisplayString>({(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_})</DisplayString>
     <Expand>
-      <ExpandedItem>c</ExpandedItem>
-      <Item Name="[comp]">comp</Item>
+      <Item Name="[0]">(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_</Item>
+      <Item Name="[1]">(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_</Item>
+    </Expand>
+  </Type>
+
+  <Type Name="std::__1::tuple&lt;*,*,*&gt;">
+    <DisplayString>({(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;2,$T3,0&gt;)__base_})</DisplayString>
+    <Expand>
+      <Item Name="[0]">(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_</Item>
+      <Item Name="[1]">(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_</Item>
+      <Item Name="[2]">(std::__1::__tuple_leaf&lt;2,$T3,0&gt;)__base_</Item>
+    </Expand>
+  </Type>
+
+  <Type Name="std::__1::tuple&lt;*,*,*,*&gt;">
+    <DisplayString>({(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;2,$T3,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;3,$T4,0&gt;)__base_})</DisplayString>
+    <Expand>
+      <Item Name="[0]">(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_</Item>
+      <Item Name="[1]">(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_</Item>
+      <Item Name="[2]">(std::__1::__tuple_leaf&lt;2,$T3,0&gt;)__base_</Item>
+      <Item Name="[3]">(std::__1::__tuple_leaf&lt;3,$T4,0&gt;)__base_</Item>
+    </Expand>
+  </Type>
+
+  <Type Name="std::__1::tuple&lt;*,*,*,*,*&gt;">
+    <DisplayString>({(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;2,$T3,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;3,$T4,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;4,$T5,0&gt;)__base_})</DisplayString>
+    <Expand>
+      <Item Name="[0]">(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_</Item>
+      <Item Name="[1]">(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_</Item>
+      <Item Name="[2]">(std::__1::__tuple_leaf&lt;2,$T3,0&gt;)__base_</Item>
+      <Item Name="[3]">(std::__1::__tuple_leaf&lt;3,$T4,0&gt;)__base_</Item>
+      <Item Name="[4]">(std::__1::__tuple_leaf&lt;4,$T5,0&gt;)__base_</Item>
+    </Expand>
+  </Type>
+
+  <Type Name="std::__1::tuple&lt;*,*,*,*,*,*&gt;">
+    <DisplayString>({(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;2,$T3,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;3,$T4,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;4,$T5,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;5,$T6,0&gt;)__base_})</DisplayString>
+    <Expand>
+      <Item Name="[0]">(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_</Item>
+      <Item Name="[1]">(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_</Item>
+      <Item Name="[2]">(std::__1::__tuple_leaf&lt;2,$T3,0&gt;)__base_</Item>
+      <Item Name="[3]">(std::__1::__tuple_leaf&lt;3,$T4,0&gt;)__base_</Item>
+      <Item Name="[4]">(std::__1::__tuple_leaf&lt;4,$T5,0&gt;)__base_</Item>
+      <Item Name="[5]">(std::__1::__tuple_leaf&lt;5,$T6,0&gt;)__base_</Item>
+    </Expand>
+  </Type>
+
+  <Type Name="std::__1::tuple&lt;*,*,*,*,*,*,*&gt;">
+    <DisplayString>({(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;2,$T3,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;3,$T4,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;4,$T5,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;5,$T6,0&gt;)__base_}, {(std::__1::__tuple_leaf&lt;6,$T7,0&gt;)__base_})</DisplayString>
+    <Expand>
+      <Item Name="[0]">(std::__1::__tuple_leaf&lt;0,$T1,0&gt;)__base_</Item>
+      <Item Name="[1]">(std::__1::__tuple_leaf&lt;1,$T2,0&gt;)__base_</Item>
+      <Item Name="[2]">(std::__1::__tuple_leaf&lt;2,$T3,0&gt;)__base_</Item>
+      <Item Name="[3]">(std::__1::__tuple_leaf&lt;3,$T4,0&gt;)__base_</Item>
+      <Item Name="[4]">(std::__1::__tuple_leaf&lt;4,$T5,0&gt;)__base_</Item>
+      <Item Name="[5]">(std::__1::__tuple_leaf&lt;5,$T6,0&gt;)__base_</Item>
+      <Item Name="[6]">(std::__1::__tuple_leaf&lt;6,$T7,0&gt;)__base_</Item>
     </Expand>
   </Type>
 
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 63e104d..7adce85 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200512.0.1
\ No newline at end of file
+0.20200512.1.1
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 63e104d..7adce85 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200512.0.1
\ No newline at end of file
+0.20200512.1.1
\ No newline at end of file
diff --git a/cc/metrics/frame_sequence_metrics.cc b/cc/metrics/frame_sequence_metrics.cc
index 205d072..0cefe55 100644
--- a/cc/metrics/frame_sequence_metrics.cc
+++ b/cc/metrics/frame_sequence_metrics.cc
@@ -177,7 +177,7 @@
   // throughput.
   aggregated_throughput_.frames_expected = impl_throughput_.frames_expected;
   DCHECK_LE(aggregated_throughput_.frames_produced,
-            aggregated_throughput_.frames_produced);
+            aggregated_throughput_.frames_expected);
 }
 
 void FrameSequenceMetrics::ReportMetrics() {
@@ -281,12 +281,12 @@
   }
 
   // Reset the metrics that reach reporting threshold.
-  if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric)
+  if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) {
     impl_throughput_ = {};
+    aggregated_throughput_ = {};
+  }
   if (main_throughput_.frames_expected >= kMinFramesForThroughputMetric)
     main_throughput_ = {};
-  if (aggregated_throughput_percent.has_value())
-    aggregated_throughput_ = {};
 }
 
 base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram(
diff --git a/cc/metrics/frame_sequence_metrics_unittest.cc b/cc/metrics/frame_sequence_metrics_unittest.cc
index b52a810..ae43da1 100644
--- a/cc/metrics/frame_sequence_metrics_unittest.cc
+++ b/cc/metrics/frame_sequence_metrics_unittest.cc
@@ -23,6 +23,18 @@
   EXPECT_EQ(first.aggregated_throughput().frames_expected, 200u);
 }
 
+TEST(FrameSequenceMetricsTest, AggregatedThroughputClearedAfterReport) {
+  FrameSequenceMetrics first(FrameSequenceTrackerType::kCompositorAnimation,
+                             nullptr);
+  first.impl_throughput().frames_expected = 200u;
+  first.impl_throughput().frames_produced = 190u;
+  first.aggregated_throughput().frames_produced = 150u;
+
+  first.ReportMetrics();
+  EXPECT_EQ(first.aggregated_throughput().frames_expected, 0u);
+  EXPECT_EQ(first.aggregated_throughput().frames_produced, 0u);
+}
+
 TEST(FrameSequenceMetricsTest, MergeMetrics) {
   // Create a metric with only a small number of frames. It shouldn't report any
   // metrics.
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 16acea16..479fc65 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1346,6 +1346,7 @@
   "java/src/org/chromium/chrome/browser/push_messaging/PushMessagingServiceObserver.java",
   "java/src/org/chromium/chrome/browser/query_tiles/FakeTileProvider.java",
   "java/src/org/chromium/chrome/browser/query_tiles/QueryTileSection.java",
+  "java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java",
   "java/src/org/chromium/chrome/browser/query_tiles/TileProviderFactory.java",
   "java/src/org/chromium/chrome/browser/rappor/RapporServiceBridge.java",
   "java/src/org/chromium/chrome/browser/resources/ResourceMapper.java",
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAccessibilityIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAccessibilityIntegrationTest.java
index e2e4520..b2cfad9 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAccessibilityIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAccessibilityIntegrationTest.java
@@ -39,6 +39,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.autofill_assistant.proto.ActionProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ChipProto;
@@ -107,6 +108,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/1081604")
     public void testBottomSheetHasRestrictedFixedHeight() throws Exception {
         ArrayList<ActionProto> list = new ArrayList<>();
 
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
index 60387306..d347f3c4 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
@@ -338,6 +338,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "https://crbug.com/1081608")
     public void interactingWithLocationBarHidesAutofillAssistant() throws Exception {
         ArrayList<ActionProto> list = new ArrayList<>();
         list.add((ActionProto) ActionProto.newBuilder()
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantKeyboardIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantKeyboardIntegrationTest.java
index dfa5887..86e32e8 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantKeyboardIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantKeyboardIntegrationTest.java
@@ -237,4 +237,61 @@
         tapElement(mTestRule, "profile_name");
         waitUntilKeyboardMatchesCondition(mTestRule, /* isShowing= */ true);
     }
+
+    @Test
+    @MediumTest
+    public void keyboardDoesNotShowOnElementClickInIFrame() throws Exception {
+        ElementReferenceProto element = (ElementReferenceProto) ElementReferenceProto.newBuilder()
+                                                .addSelectors("#iframe")
+                                                .addSelectors("#name")
+                                                .build();
+
+        ArrayList<ActionProto> list = new ArrayList<>();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setClick(ClickProto.newBuilder()
+                                           .setClickType(ClickType.CLICK)
+                                           .setElementToClick(element))
+                         .build());
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setPrompt(PromptProto.newBuilder().setMessage("Clicked").addChoices(
+                                 Choice.newBuilder().setChip(
+                                         ChipProto.newBuilder()
+                                                 .setType(ChipType.HIGHLIGHTED_ACTION)
+                                                 .setText("Continue"))))
+                         .build());
+        list.add(
+                (ActionProto) ActionProto.newBuilder()
+                        .setFocusElement(FocusElementProto.newBuilder()
+                                                 .setElement(element)
+                                                 .setTouchableElementArea(
+                                                         ElementAreaProto.newBuilder().addTouchable(
+                                                                 Rectangle.newBuilder().addElements(
+                                                                         element))))
+                        .build());
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setPrompt(PromptProto.newBuilder()
+                                            .setMessage("Highlighted")
+                                            .addChoices(Choice.newBuilder()))
+                         .build());
+
+        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
+                (SupportedScriptProto) SupportedScriptProto.newBuilder()
+                        .setPath(TEST_PAGE)
+                        .setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
+                                ChipProto.newBuilder().setText("Done")))
+                        .build(),
+                list);
+
+        runAutofillAssistant(script);
+
+        // Autofill Assistant clicking an <input> element does not show the keyboard.
+        waitUntilViewMatchesCondition(withText("Clicked"), isCompletelyDisplayed());
+        assertThat(isKeyboardVisible(), is(false));
+
+        // A user's click on an <input> element does show the keyboard.
+        onView(withText("Continue")).perform(click());
+        waitUntilViewMatchesCondition(withText("Highlighted"), isCompletelyDisplayed());
+        tapElement(mTestRule, "iframe", "name");
+        waitUntilKeyboardMatchesCondition(mTestRule, /* isShowing= */ true);
+    }
 }
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
index 10cc489..fce0cd8 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
@@ -48,6 +48,7 @@
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
@@ -173,6 +174,7 @@
     @Test
     @MediumTest
     @Feature({"StartSurface"})
+    @DisabledTest
     // clang-format off
     @CommandLineFlags.Add({BASE_PARAMS + "/omniboxonly" +
             "/hide_switch_when_no_incognito_tabs/true"})
@@ -225,6 +227,7 @@
     @Test
     @MediumTest
     @Feature({"StartSurface"})
+    @DisabledTest
     @CommandLineFlags.Add({BASE_PARAMS + "/twopanes"})
     public void testShow_TwoPanes() {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripTest.java
index 3b25bdb..ce09a7e5 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripTest.java
@@ -42,6 +42,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.TabsTest.SimulateClickOnMainThread;
@@ -403,6 +404,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/1081697")
     public void testStrip_UndoDismiss() throws Exception {
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         for (int i = 0; i < 3; i++) {
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImpl.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImpl.java
index 6deb83d..0ae18dc 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImpl.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImpl.java
@@ -369,6 +369,7 @@
                     actions.add(StreamUploadableAction.newBuilder()
                                         .setFeatureContentId(contentId)
                                         .setPayload(viewActionData.actionPayload)
+                                        .setDurationMs(viewActionData.durationMs)
                                         .setTimestampSeconds(currentTimeS)
                                         .build());
                     // Stop tracking this particular content as we're already uploading a view
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilder.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilder.java
index 74fd14c..8db0e01 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilder.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilder.java
@@ -68,6 +68,7 @@
                         FeedAction.newBuilder().setActionPayload(payload).setClientData(
                                 FeedAction.ClientData.newBuilder()
                                         .setTimestampSeconds(action.getTimestampSeconds())
+                                        .setDurationMs(action.getDurationMs())
                                         .build());
                 if (mSemanticPropertiesMap.containsKey(contentId)) {
                     feedAction.setSemanticProperties(
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImplTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImplTest.java
index ff02361a..78828d5 100644
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImplTest.java
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedactionmanager/FeedActionManagerImplTest.java
@@ -281,7 +281,7 @@
                 // View covers >50% (FeedActionManagerImpl.VIEWPORT_COVERAGE_THRESHOLD) of viewport,
                 // but <50% (FeedActionManagerImpl.VIEW_EXPOSURE_THRESHOLD) of the view is visible.
                 new Rect(0, -1000, 100, 51), CONTENT_ID_STRING, LONG_DURATION_MS);
-        verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S);
+        verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S, LONG_DURATION_MS);
     }
 
     @Test
@@ -291,7 +291,7 @@
                 // it covers <50% (FeedActionManagerImpl.VIEWPORT_COVERAGE_THRESHOLD) of the
                 // viewport.
                 new Rect(0, -48, 100, 49), CONTENT_ID_STRING, LONG_DURATION_MS);
-        verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S);
+        verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S, LONG_DURATION_MS);
     }
 
     @Test
@@ -351,12 +351,14 @@
                                       .setFeatureContentId(CONTENT_ID_STRING)
                                       .setPayload(ACTION_PAYLOAD)
                                       .setTimestampSeconds(DEFAULT_TIME_SECONDS + LONG_DURATION_S)
+                                      .setDurationMs(LONG_DURATION_MS)
                                       .build(),
                 StreamUploadableAction.newBuilder()
                         .setFeatureContentId(CONTENT_ID_STRING)
                         .setPayload(ACTION_PAYLOAD)
                         .setTimestampSeconds(
                                 DEFAULT_TIME_SECONDS + LONG_DURATION_S + 1 + LONG_DURATION_S)
+                        .setDurationMs(LONG_DURATION_MS)
                         .build());
     }
 
@@ -411,11 +413,13 @@
                                       .setFeatureContentId("contentId1")
                                       .setPayload(ACTION_PAYLOAD)
                                       .setTimestampSeconds(DEFAULT_TIME_SECONDS + LONG_DURATION_S)
+                                      .setDurationMs(LONG_DURATION_MS)
                                       .build(),
                 StreamUploadableAction.newBuilder()
                         .setFeatureContentId("contentId3")
                         .setPayload(ACTION_PAYLOAD)
                         .setTimestampSeconds(DEFAULT_TIME_SECONDS + LONG_DURATION_S)
+                        .setDurationMs(LONG_DURATION_MS)
                         .build());
     }
 
@@ -442,7 +446,8 @@
         mActionManager.onScrollEnd();
         mFakeClock.advance(1000);
         mActionManager.onHide();
-        verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S + 1);
+        verifyActionUpserted(
+                ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S + 1, LONG_DURATION_MS);
     }
 
     @Test
@@ -471,7 +476,7 @@
         mActionManager.onAnimationFinished();
         mFakeClock.advance(LONG_DURATION_MS);
         mActionManager.onHide();
-        verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S);
+        verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S, LONG_DURATION_MS);
     }
 
     @Test
@@ -500,7 +505,8 @@
         mActionManager.onLayoutChange();
         mFakeClock.advance(1000);
         mActionManager.onHide();
-        verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S + 1);
+        verifyActionUpserted(
+                ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S + 1, LONG_DURATION_MS);
     }
 
     @Test
@@ -528,7 +534,8 @@
         mActionManager.onLayoutChange();
         verifyNoActionUpserted();
         mActionManager.storeViewActions(() -> {
-            verifyActionUpserted(ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S);
+            verifyActionUpserted(
+                    ACTION_PAYLOAD, CONTENT_ID_STRING, LONG_DURATION_S, LONG_DURATION_MS);
             mStoreViewActionsRunnable.run();
         });
         verify(mStoreViewActionsRunnable).run();
@@ -543,11 +550,13 @@
         verify(mStoreViewActionsRunnable, times(1)).run();
     }
 
-    private void verifyActionUpserted(ActionPayload payload, String contentId, long durationS) {
+    private void verifyActionUpserted(
+            ActionPayload payload, String contentId, long elapsedTimeS, long durationMs) {
         verifyActionsUpserted(StreamUploadableAction.newBuilder()
                                       .setFeatureContentId(contentId)
                                       .setPayload(payload)
-                                      .setTimestampSeconds(DEFAULT_TIME_SECONDS + durationS)
+                                      .setTimestampSeconds(DEFAULT_TIME_SECONDS + elapsedTimeS)
+                                      .setDurationMs(durationMs)
                                       .build());
     }
 
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilderTest.java
index fcb47b4..1e5d460 100644
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilderTest.java
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/library/feedrequestmanager/UploadableActionsRequestBuilderTest.java
@@ -37,6 +37,7 @@
 public class UploadableActionsRequestBuilderTest {
     private static final String CONTENT_ID = "contentId";
     private static final int TIME = 100;
+    private static final long DURATION = 20;
     private static final byte[] SEMANTIC_PROPERTIES_BYTES = new byte[] {0x1, 0xf};
     private static final SemanticProperties SEMANTIC_PROPERTIES =
             SemanticProperties.newBuilder()
@@ -68,6 +69,7 @@
                                .setFeatureContentId(CONTENT_ID)
                                .setPayload(mPayload)
                                .setTimestampSeconds(TIME)
+                               .setDurationMs(DURATION)
                                .build());
         mRequestBuilder = ActionRequest.newBuilder().setRequestVersion(
                 ActionRequest.RequestVersion.FEED_UPLOAD_ACTION);
@@ -81,6 +83,7 @@
                                         .setActionPayload(mPayload)
                                         .setClientData(FeedAction.ClientData.newBuilder()
                                                                .setTimestampSeconds(TIME)
+                                                               .setDurationMs(DURATION)
                                                                .build())
                                         .build();
         mFeedActionRequestBuilder.addFeedAction(feedAction);
@@ -111,6 +114,7 @@
                                         .setActionPayload(mPayload)
                                         .setClientData(FeedAction.ClientData.newBuilder()
                                                                .setTimestampSeconds(TIME)
+                                                               .setDurationMs(DURATION)
                                                                .build())
                                         .build();
         mFeedActionRequestBuilder.addFeedAction(feedAction);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
index 63777e95..503d24b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
@@ -22,7 +22,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabBuilder;
 import org.chromium.chrome.browser.tab.TabLaunchType;
-import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tab_activity_glue.ReparentingTask;
 import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.content_public.browser.LoadUrlParams;
@@ -44,24 +43,46 @@
         public final CustomTabsSessionToken session;
         public final String url;
         public final Tab tab;
-        public final TabObserver observer;
         public final String referrer;
 
-        private SpeculationParams(CustomTabsSessionToken session, String url, Tab tab,
-                TabObserver observer, String referrer) {
+        private SpeculationParams(
+                CustomTabsSessionToken session, String url, Tab tab, String referrer) {
             this.session = session;
             this.url = url;
             this.tab = tab;
-            this.observer = observer;
             this.referrer = referrer;
         }
     }
 
     private class HiddenTabObserver extends EmptyTabObserver {
+        // This WindowAndroid is "owned" by the Tab and should be destroyed when it is no longer
+        // needed by the Tab or when the Tab is destroyed.
+        private WindowAndroid mOwnedWindowAndroid;
+        public HiddenTabObserver(WindowAndroid ownedWindowAndroid) {
+            mOwnedWindowAndroid = ownedWindowAndroid;
+        }
+
         @Override
         public void onCrash(Tab tab) {
             destroyHiddenTab(null);
         }
+
+        @Override
+        public void onDestroyed(Tab tab) {
+            destroyOwnedWindow(tab);
+        }
+
+        @Override
+        public void onActivityAttachmentChanged(Tab tab, WindowAndroid window) {
+            destroyOwnedWindow(tab);
+        }
+
+        private void destroyOwnedWindow(Tab tab) {
+            assert mOwnedWindowAndroid != null;
+            mOwnedWindowAndroid.destroy();
+            mOwnedWindowAndroid = null;
+            tab.removeObserver(this);
+        }
     }
 
     @Nullable private SpeculationParams mSpeculation;
@@ -85,7 +106,7 @@
 
         Tab tab = buildDetachedTab();
 
-        HiddenTabObserver observer = new HiddenTabObserver();
+        HiddenTabObserver observer = new HiddenTabObserver(tab.getWindowAndroid());
         tab.addObserver(observer);
 
         // Updating post message as soon as we have a valid WebContents.
@@ -101,7 +122,7 @@
             loadParams.setReferrer(new Referrer(referrer, ReferrerPolicy.DEFAULT));
         }
 
-        mSpeculation = new SpeculationParams(session, url, tab, observer, referrer);
+        mSpeculation = new SpeculationParams(session, url, tab, referrer);
         mSpeculation.tab.loadUrl(loadParams);
     }
 
@@ -151,7 +172,6 @@
             String speculatedUrl = mSpeculation.url;
             String speculationReferrer = mSpeculation.referrer;
 
-            tab.removeObserver(mSpeculation.observer);
             mSpeculation = null;
 
             boolean urlsMatch = ignoreFragments
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index 02fe3dc..996e0a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -52,6 +52,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.tiles.TileSuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.query_tiles.QueryTileUtils;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.tab.Tab;
@@ -666,8 +667,9 @@
 
     /** Called when a query tile is selected by the user. */
     void onQueryTileSelected(QueryTile queryTile) {
-        // For last level tile, start a search query.
-        if (queryTile.children.isEmpty()) {
+        // For last level tile, start a search query, unless we want to let user have a chance to
+        // edit the query.
+        if (queryTile.children.isEmpty() && !QueryTileUtils.isQueryEditingEnabled()) {
             String url = TemplateUrlServiceFactory.get().getUrlForSearchQuery(queryTile.queryText);
             mDelegate.loadUrl(url, PageTransition.LINK, mLastActionUpTimestamp);
             mDelegate.setKeyboardVisibility(false);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java
index bcc4f897..5d580f4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java
@@ -36,8 +36,11 @@
 import org.chromium.components.page_info.PageInfoControllerDelegate;
 import org.chromium.components.page_info.PageInfoControllerDelegate.OfflinePageState;
 import org.chromium.components.page_info.PageInfoControllerDelegate.PreviewPageState;
+import org.chromium.components.page_info.PageInfoFeatureList;
+import org.chromium.components.page_info.PageInfoRowView;
 import org.chromium.components.page_info.PageInfoView;
 import org.chromium.components.page_info.PageInfoView.PageInfoViewParams;
+import org.chromium.components.page_info.PageInfoViewV2;
 import org.chromium.components.page_info.SystemSettingsActivityRequiredListener;
 import org.chromium.components.security_state.ConnectionSecurityLevel;
 import org.chromium.components.security_state.SecurityStateModel;
@@ -301,7 +304,17 @@
     @Override
     public void updatePermissionDisplay(PageInfoView view) {
         assert (mPermissionParamsListBuilder != null);
-        view.setPermissions(mPermissionParamsListBuilder.build());
+        PageInfoView.PermissionParams params = mPermissionParamsListBuilder.build();
+        if (PageInfoFeatureList.isEnabled(PageInfoFeatureList.PAGE_INFO_V2)) {
+            PageInfoRowView.ViewParams rowParams = new PageInfoRowView.ViewParams();
+            rowParams.visible = true;
+            rowParams.title = mContext.getString(R.string.page_info_permissions_title);
+            // TODO(crbug.com/1077766): Create a permissions subtitle string that represents
+            // the state, potentially using R.plurals.
+            ((PageInfoViewV2) view).getPermissionsRowView().setParams(rowParams);
+        } else {
+            view.setPermissions(params);
+        }
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileSection.java b/chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileSection.java
index 97c1cdf..78a62c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileSection.java
@@ -65,7 +65,7 @@
         mQueryTileSectionView = queryTileSectionView;
         mSearchBoxCoordinator = searchBoxCoordinator;
         mSubmitQueryCallback = performSearchQueryCallback;
-        if (!isFeatureEnabled()) return;
+        if (!QueryTileUtils.isFeatureEnabled()) return;
 
         mTileProvider = TileProviderFactory.getForProfile(profile);
         TileConfig tileConfig = new TileConfig.Builder().setUmaPrefix(UMA_PREFIX).build();
@@ -83,9 +83,14 @@
     private void onTileClicked(ImageTile tile) {
         QueryTile queryTile = (QueryTile) tile;
         mTileUmaLogger.recordTileClicked(queryTile);
+
         boolean isLastLevelTile = queryTile.children.isEmpty();
         if (isLastLevelTile) {
-            mSubmitQueryCallback.onResult(queryTile.queryText);
+            if (QueryTileUtils.isQueryEditingEnabled()) {
+                mSearchBoxCoordinator.setSearchText(queryTile.queryText);
+            } else {
+                mSubmitQueryCallback.onResult(queryTile.queryText);
+            }
             return;
         }
 
@@ -158,7 +163,7 @@
      *         tiles section on NTP is shortened so that feed is still visible above the fold.
      */
     public Integer getMaxRowsForMostVisitedTiles() {
-        if (!isFeatureEnabled()) return null;
+        if (!QueryTileUtils.isFeatureEnabled()) return null;
 
         DisplayAndroid display =
                 DisplayAndroid.getNonMultiDisplay(mQueryTileSectionView.getContext());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java
new file mode 100644
index 0000000..0e37b2d
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.query_tiles;
+
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+
+/**
+ * Handles various feature utility functions for query tiles.
+ */
+public class QueryTileUtils {
+    /**
+     * This is the main feature flag for query tiles. All other flags are effective only when this
+     * flag is enabled.
+     * @return Whether the query tile feature is enabled.
+     */
+    public static boolean isFeatureEnabled() {
+        return ChromeFeatureList.isEnabled(ChromeFeatureList.QUERY_TILES);
+    }
+
+    /**
+     * This is one experimental variation where user will have a chance of editing the query text
+     * before starting a search. When a query tile is clicked, the query text will be pasted in the
+     * omnibox. No second level tiles will be displayed. This is meant to show only one level of
+     * query tiles.
+     * @return Whether the user should have a chance to edit the query text before starting a
+     *         search.
+     */
+    public static boolean isQueryEditingEnabled() {
+        return ChromeFeatureList.isEnabled(ChromeFeatureList.QUERY_TILES_ENABLE_QUERY_EDITING);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoControllerTest.java
index 2e62ce0..247c800 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoControllerTest.java
@@ -96,9 +96,9 @@
                             new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(tab));
             chromePageInfoControllerDelegate.setOfflinePageStateForTesting(
                     ChromePageInfoControllerDelegate.OfflinePageState.NOT_OFFLINE_PAGE);
-            PageInfoController pageInfo =
-                    new PageInfoController(tab.getWebContents(), ConnectionSecurityLevel.NONE,
-                            /*publisher=*/null, chromePageInfoControllerDelegate);
+            PageInfoController pageInfo = new PageInfoController(tab.getWebContents(),
+                    ConnectionSecurityLevel.NONE,
+                    /*publisher=*/null, chromePageInfoControllerDelegate, /*isV2Enabled=*/false);
             PageInfoView pageInfoView = pageInfo.getPageInfoViewForTesting();
             // Test that the title contains the Unicode hostname rather than strict equality, as
             // the test server will be bound to a random port.
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index e20a219..aa5e446 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3031,6 +3031,7 @@
       "//components/location/android:settings",
       "//components/module_installer/android:native",
       "//components/omnibox/browser",
+      "//components/page_info/android",
       "//components/page_load_metrics/browser",
       "//components/paint_preview/browser/android",
       "//components/paint_preview/player/android",
@@ -3220,6 +3221,8 @@
       "enterprise/connectors/connectors_prefs.h",
       "enterprise/connectors/enterprise_connectors_policy_handler.cc",
       "enterprise/connectors/enterprise_connectors_policy_handler.h",
+      "enterprise/connectors/service_providers.cc",
+      "enterprise/connectors/service_providers.h",
       "enterprise/reporting/browser_report_generator.cc",
       "enterprise/reporting/browser_report_generator.h",
       "enterprise/reporting/extension_info.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index fde4757..cc6ceac 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -102,6 +102,7 @@
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "components/page_info/android/features.h"
 #include "components/paint_preview/buildflags/buildflags.h"
 #include "components/paint_preview/features/features.h"
 #include "components/password_manager/core/common/password_manager_features.h"
@@ -2849,6 +2850,10 @@
     {"query-tiles-omnibox", flag_descriptions::kQueryTilesOmniboxName,
      flag_descriptions::kQueryTilesOmniboxDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(upboarding::features::kQueryTilesInOmnibox)},
+    {"query-tiles-enable-query-editing",
+     flag_descriptions::kQueryTilesEnableQueryEditingName,
+     flag_descriptions::kQueryTilesEnableQueryEditingDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(upboarding::features::kQueryTilesEnableQueryEditing)},
     {"query-tiles-country-code", flag_descriptions::kQueryTilesCountryCode,
      flag_descriptions::kQueryTilesCountryCodeDescription, kOsAndroid,
      MULTI_VALUE_TYPE(kQueryTilesCountryChoices)},
@@ -3550,6 +3555,11 @@
      flag_descriptions::kCrossOriginOpenerPolicyDescription, kOsAll,
      FEATURE_VALUE_TYPE(network::features::kCrossOriginOpenerPolicy)},
 
+    {"cross-origin-opener-policy-reporting",
+     flag_descriptions::kCrossOriginOpenerPolicyReportingName,
+     flag_descriptions::kCrossOriginOpenerPolicyReportingDescription, kOsAll,
+     FEATURE_VALUE_TYPE(network::features::kCrossOriginOpenerPolicyReporting)},
+
     {"disable-keepalive-fetch", flag_descriptions::kDisableKeepaliveFetchName,
      flag_descriptions::kDisableKeepaliveFetchDescription, kOsAll,
      FEATURE_VALUE_TYPE(network::features::kDisableKeepaliveFetch)},
@@ -5389,6 +5399,12 @@
      FEATURE_VALUE_TYPE(chrome::android::kPageInfoPerformanceHints)},
 #endif  // !defined(OS_ANDROID)
 
+#if defined(OS_ANDROID)
+    {"page-info-version-2", flag_descriptions::kPageInfoV2Name,
+     flag_descriptions::kPageInfoV2Description, kOsAndroid,
+     FEATURE_VALUE_TYPE(page_info::kPageInfoV2)},
+#endif  // !defined(OS_ANDROID)
+
 #if defined(OS_CHROMEOS)
     {"drag-to-snap-in-clamshell-mode",
      flag_descriptions::kDragToSnapInClamshellModeName,
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc b/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc
index fb0138a..751f8cb2 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc
@@ -122,11 +122,15 @@
           env, base::android::ToJavaIntArray(env, ints.get(),
                                              proto.ints().values_size()));
     }
-    case ValueProto::kUserActions: {
+    case ValueProto::kCreditCards:
+    case ValueProto::kProfiles:
+    case ValueProto::kLoginOptions:
+    case ValueProto::kCreditCardResponse:
+    case ValueProto::kLoginOptionResponse:
+    case ValueProto::kUserActions:
       // Unused.
       NOTREACHED();
       return nullptr;
-    }
     case ValueProto::kDates: {
       auto jlist = Java_AssistantValue_createDateTimeList(env);
       for (const auto& value : proto.dates().values()) {
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
index 1fe7de7..85e55d9 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
diff --git a/chrome/browser/autofill/autofill_server_browsertest.cc b/chrome/browser/autofill/autofill_server_browsertest.cc
index b662a8e..6f32b1e 100644
--- a/chrome/browser/autofill/autofill_server_browsertest.cc
+++ b/chrome/browser/autofill/autofill_server_browsertest.cc
@@ -75,22 +75,17 @@
   }
 
  private:
-  // Helper to extract the value of a query param. Returns "*** not found ***"
-  // if the requested query param is not in the query string.
-  std::string GetQueryParam(const std::string& query_str,
-                            const std::string& param_name) {
-    url::Component query(0, query_str.length());
-    url::Component key, value;
-    while (url::ExtractQueryKeyValue(query_str.c_str(), &query, &key, &value)) {
-      std::string key_string(query_str.substr(key.begin, key.len));
-      std::string param_text(query_str.substr(value.begin, value.len));
-      std::string param_value;
-      if (key_string == param_name &&
-          base::Base64UrlDecode(param_text,
-                                base::Base64UrlDecodePolicy::REQUIRE_PADDING,
-                                &param_value)) {
-        return param_value;
-      }
+  // Helper to extract the value passed to a lookup in the URL. Returns "*** not
+  // found ***" if the the data cannot be decoded.
+  std::string GetLookupContent(const std::string& query_path) {
+    if (query_path.find("/v1/pages/") == std::string::npos)
+      return "*** not found ***";
+    std::string payload = query_path.substr(strlen("/v1/pages/"));
+    std::string decoded_payload;
+    if (base::Base64UrlDecode(payload,
+                              base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+                              &decoded_payload)) {
+      return decoded_payload;
     }
     return "*** not found ***";
   }
@@ -99,7 +94,7 @@
     // NOTE: This constant matches the one defined in
     // components/autofill/core/browser/autofill_download_manager.cc
     static const char kDefaultAutofillServerURL[] =
-        "https://clients1.google.com/tbproxy/af/";
+        "https://content-autofill.googleapis.com/";
     DCHECK(params);
     network::ResourceRequest resource_request = params->url_request;
     if (resource_request.url.spec().find(kDefaultAutofillServerURL) ==
@@ -109,7 +104,7 @@
 
     const std::string& data =
         (resource_request.method == "GET")
-            ? GetQueryParam(resource_request.url.query(), "q")
+            ? GetLookupContent(resource_request.url.path())
             : network::GetUploadData(resource_request);
     EXPECT_EQ(data, expected_upload_data_);
 
@@ -185,15 +180,15 @@
       "  };"
       "</script>";
 
-  AutofillQueryContents query;
+  AutofillPageQueryRequest query;
   query.set_client_version("6.1.1715.1442/en (GGLL)");
-  AutofillQueryContents::Form* query_form = query.add_form();
+  auto* query_form = query.add_forms();
   query_form->set_signature(15916856893790176210U);
 
-  test::FillQueryField(query_form->add_field(), 2594484045U, "one", "text");
-  test::FillQueryField(query_form->add_field(), 2750915947U, "two", "text");
-  test::FillQueryField(query_form->add_field(), 3494787134U, "three", "text");
-  test::FillQueryField(query_form->add_field(), 1236501728U, "four", "text");
+  test::FillQueryField(query_form->add_fields(), 2594484045U, "one", "text");
+  test::FillQueryField(query_form->add_fields(), 2750915947U, "two", "text");
+  test::FillQueryField(query_form->add_fields(), 3494787134U, "three", "text");
+  test::FillQueryField(query_form->add_fields(), 1236501728U, "four", "text");
 
   std::string expected_query_string;
   ASSERT_TRUE(query.SerializeToString(&expected_query_string));
@@ -207,30 +202,31 @@
   // Submit the form, using a simulated mouse click because form submissions not
   // triggered by user gestures are ignored. Expect an upload request upon form
   // submission, with form fields matching those from the query request.
-  AutofillUploadContents upload;
-  upload.set_submission(true);
-  upload.set_client_version("6.1.1715.1442/en (GGLL)");
-  upload.set_form_signature(15916856893790176210U);
-  upload.set_autofill_used(false);
-  upload.set_data_present("1f7e0003780000080004");
-  upload.set_action_signature(15724779818122431245U);
-  upload.set_form_name("test_form");
-  upload.set_passwords_revealed(false);
-  upload.set_submission_event(
+  AutofillUploadRequest request;
+  AutofillUploadContents* upload = request.mutable_upload();
+  upload->set_submission(true);
+  upload->set_client_version("6.1.1715.1442/en (GGLL)");
+  upload->set_form_signature(15916856893790176210U);
+  upload->set_autofill_used(false);
+  upload->set_data_present("1f7e0003780000080004");
+  upload->set_action_signature(15724779818122431245U);
+  upload->set_form_name("test_form");
+  upload->set_passwords_revealed(false);
+  upload->set_submission_event(
       AutofillUploadContents_SubmissionIndicatorEvent_HTML_FORM_SUBMISSION);
-  upload.set_has_form_tag(true);
+  upload->set_has_form_tag(true);
 
-  test::FillUploadField(upload.add_field(), 2594484045U, "one", "text", nullptr,
-                        2U);
-  test::FillUploadField(upload.add_field(), 2750915947U, "two", "text", "off",
-                        2U);
-  test::FillUploadField(upload.add_field(), 3494787134U, "three", "text",
+  test::FillUploadField(upload->add_field(), 2594484045U, "one", "text",
                         nullptr, 2U);
-  test::FillUploadField(upload.add_field(), 1236501728U, "four", "text", "off",
+  test::FillUploadField(upload->add_field(), 2750915947U, "two", "text", "off",
+                        2U);
+  test::FillUploadField(upload->add_field(), 3494787134U, "three", "text",
+                        nullptr, 2U);
+  test::FillUploadField(upload->add_field(), 1236501728U, "four", "text", "off",
                         2U);
 
   std::string expected_upload_string;
-  ASSERT_TRUE(upload.SerializeToString(&expected_upload_string));
+  ASSERT_TRUE(request.SerializeToString(&expected_upload_string));
 
   WindowedNetworkObserver upload_network_observer(expected_upload_string);
   content::WebContents* web_contents =
@@ -254,14 +250,14 @@
       "  <input type='submit'>"
       "</form>";
 
-  AutofillQueryContents query;
+  AutofillPageQueryRequest query;
   query.set_client_version("6.1.1715.1442/en (GGLL)");
-  AutofillQueryContents::Form* query_form = query.add_form();
+  auto* query_form = query.add_forms();
   query_form->set_signature(8900697631820480876U);
 
-  test::FillQueryField(query_form->add_field(), 2594484045U, "one", "text");
-  test::FillQueryField(query_form->add_field(), 2750915947U, "two", "text");
-  test::FillQueryField(query_form->add_field(), 116843943U, "three",
+  test::FillQueryField(query_form->add_fields(), 2594484045U, "one", "text");
+  test::FillQueryField(query_form->add_fields(), 2750915947U, "two", "text");
+  test::FillQueryField(query_form->add_fields(), 116843943U, "three",
                        "password");
 
   std::string expected_query_string;
diff --git a/chrome/browser/browser_switcher/browser_switcher_service.cc b/chrome/browser/browser_switcher/browser_switcher_service.cc
index 22433b0..4588b30 100644
--- a/chrome/browser/browser_switcher/browser_switcher_service.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_service.cc
@@ -30,8 +30,10 @@
 namespace {
 
 // How long to wait after |BrowserSwitcherService| is created before initiating
-// the sitelist fetch.
-const base::TimeDelta kFetchSitelistDelay = base::TimeDelta::FromSeconds(60);
+// the sitelist fetch. Non-zero values are used for testing.
+//
+// TODO(nicolaso): get rid of this.
+const base::TimeDelta kFetchSitelistDelay = base::TimeDelta();
 
 // How long to wait after a fetch to re-fetch the sitelist to keep it fresh.
 const base::TimeDelta kRefreshSitelistDelay = base::TimeDelta::FromMinutes(30);
@@ -141,6 +143,7 @@
     request->url = source.url;
     request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
     request->credentials_mode = network::mojom::CredentialsMode::kInclude;
+    request->priority = net::IDLE;
     source.url_loader = network::SimpleURLLoader::Create(std::move(request),
                                                          traffic_annotation);
     source.url_loader->SetRetryOptions(
diff --git a/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc b/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc
index 1ff0ee8..62a0c79 100644
--- a/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc
@@ -102,7 +102,6 @@
     EXPECT_CALL(provider_, IsInitializationComplete(testing::_))
         .WillRepeatedly(testing::Return(true));
     policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
-    BrowserSwitcherService::SetFetchDelayForTesting(base::TimeDelta());
     BrowserSwitcherService::SetRefreshDelayForTesting(base::TimeDelta());
   }
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index caf1fd9..180943b 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2348,12 +2348,13 @@
 
 bool ChromeContentBrowserClient::AllowAppCache(
     const GURL& manifest_url,
-    const GURL& first_party,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& top_frame_origin,
     content::BrowserContext* context) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return CookieSettingsFactory::GetForProfile(
              Profile::FromBrowserContext(context))
-      ->IsCookieAccessAllowed(manifest_url, first_party);
+      ->IsCookieAccessAllowed(manifest_url, site_for_cookies, top_frame_origin);
 }
 
 content::AllowServiceWorkerResult
@@ -2489,7 +2490,7 @@
     base::OnceCallback<void(bool)> callback) {
   Profile* profile = Profile::FromBrowserContext(browser_context);
   auto cookie_settings = CookieSettingsFactory::GetForProfile(profile);
-  bool allow = cookie_settings->IsCookieAccessAllowed(url, url);
+  bool allow = cookie_settings->IsCookieAccessAllowed(url, url, base::nullopt);
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   GuestPermissionRequestHelper(url, render_frames, std::move(callback), allow);
@@ -2555,7 +2556,7 @@
   Profile* profile = Profile::FromBrowserContext(browser_context);
   auto cookie_settings = CookieSettingsFactory::GetForProfile(profile);
 
-  bool allow = cookie_settings->IsCookieAccessAllowed(url, url);
+  bool allow = cookie_settings->IsCookieAccessAllowed(url, url, base::nullopt);
 
   // Record access to IndexedDB for potential display in UI.
   for (const auto& it : render_frames) {
@@ -2572,7 +2573,7 @@
     const std::vector<content::GlobalFrameRoutingId>& render_frames) {
   Profile* profile = Profile::FromBrowserContext(browser_context);
   auto cookie_settings = CookieSettingsFactory::GetForProfile(profile);
-  bool allow = cookie_settings->IsCookieAccessAllowed(url, url);
+  bool allow = cookie_settings->IsCookieAccessAllowed(url, url, base::nullopt);
 
   // Record access to CacheStorage for potential display in UI.
   for (const auto& it : render_frames) {
@@ -2589,7 +2590,7 @@
     const std::vector<content::GlobalFrameRoutingId>& render_frames) {
   Profile* profile = Profile::FromBrowserContext(browser_context);
   auto cookie_settings = CookieSettingsFactory::GetForProfile(profile);
-  return cookie_settings->IsCookieAccessAllowed(url, url);
+  return cookie_settings->IsCookieAccessAllowed(url, url, base::nullopt);
 }
 
 ChromeContentBrowserClient::AllowWebBluetoothResult
@@ -3699,16 +3700,6 @@
             ->GetPolicies(policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
                                                   std::string()));
 
-#if defined(OS_WIN)
-    const base::Value* force_network_in_process_value =
-        policies.GetValue(policy::key::kForceNetworkInProcess);
-    bool force_network_in_process = false;
-    if (force_network_in_process_value)
-      force_network_in_process_value->GetAsBoolean(&force_network_in_process);
-    if (force_network_in_process)
-      content::ForceInProcessNetworkService(true);
-#endif
-
     service_manager::EnableAudioSandbox(ShouldEnableAudioSandbox(policies));
   }
 #endif
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 9bf5aae..2a6c2428 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -220,7 +220,9 @@
       content::BrowserContext* browser_context,
       blink::mojom::RendererPreferences* out_prefs) override;
   bool AllowAppCache(const GURL& manifest_url,
-                     const GURL& first_party,
+
+                     const GURL& site_for_cookies,
+                     const base::Optional<url::Origin>& top_frame_origin,
                      content::BrowserContext* context) override;
   content::AllowServiceWorkerResult AllowServiceWorkerOnIO(
       const GURL& scope,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index c2889ac..fca1f91 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -794,6 +794,8 @@
     "cert_provisioning/cert_provisioning_common.h",
     "cert_provisioning/cert_provisioning_invalidator.cc",
     "cert_provisioning/cert_provisioning_invalidator.h",
+    "cert_provisioning/cert_provisioning_metrics.cc",
+    "cert_provisioning/cert_provisioning_metrics.h",
     "cert_provisioning/cert_provisioning_platform_keys_helpers.cc",
     "cert_provisioning/cert_provisioning_platform_keys_helpers.h",
     "cert_provisioning/cert_provisioning_scheduler.cc",
diff --git a/chrome/browser/chromeos/app_mode/app_session.cc b/chrome/browser/chromeos/app_mode/app_session.cc
index 9f63527c..fb6b87f 100644
--- a/chrome/browser/chromeos/app_mode/app_session.cc
+++ b/chrome/browser/chromeos/app_mode/app_session.cc
@@ -175,8 +175,13 @@
         browser->tab_strip_model()->GetActiveWebContents();
     std::string url_string =
         active_tab ? active_tab->GetURL().spec() : std::string();
+    bool app_browser = browser->is_type_app() || browser->is_type_app_popup() ||
+                       browser->is_type_popup();
 
-    if (KioskSettingsNavigationThrottle::IsSettingsPage(url_string)) {
+    // The browser has to be of type TYPE_APP, TYPE_POPUP or TYPE_APP_POPUP,
+    // since they do not have visible tab strip.
+    if (app_browser &&
+        KioskSettingsNavigationThrottle::IsSettingsPage(url_string)) {
       if (app_session_->settings_browser_ &&
           browser != app_session_->settings_browser_) {
         // Navigate to this page in the old browser, the current one will be
diff --git a/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc b/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
index 5c6b3603..4718726 100644
--- a/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
+++ b/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/apps/intent_helper/apps_navigation_types.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h"
@@ -21,7 +20,6 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
-#include "components/arc/metrics/arc_metrics_constants.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc b/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc
index 4928b77..ececd5c 100644
--- a/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc
+++ b/chrome/browser/chromeos/apps/intent_helper/common_apps_navigation_throttle.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/apps/intent_helper/apps_navigation_types.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h"
+#include "chrome/browser/chromeos/apps/metrics/intent_handling_metrics.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/intent_picker_tab_helper.h"
 #include "chrome/common/chrome_features.h"
@@ -130,6 +131,14 @@
         url, launch_source, display::kDefaultDisplayId);
     CloseOrGoBack(web_contents);
   }
+
+  apps::AppsNavigationThrottle::PickerAction action =
+      apps::AppsNavigationThrottle::GetPickerAction(entry_type, close_reason,
+                                                    should_persist);
+  apps::AppsNavigationThrottle::Platform platform =
+      apps::AppsNavigationThrottle::GetDestinationPlatform(launch_name, action);
+  apps::IntentHandlingMetrics::RecordIntentPickerMetrics(
+      apps::Source::kHttpOrHttps, should_persist, action, platform);
 }
 
 // static
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.cc
index 337937f..c29a375 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.cc
@@ -42,6 +42,18 @@
 }
 }  // namespace
 
+bool IsFinalState(CertProvisioningWorkerState state) {
+  switch (state) {
+    case CertProvisioningWorkerState::kSucceeded:
+    case CertProvisioningWorkerState::kInconsistentDataError:
+    case CertProvisioningWorkerState::kFailed:
+    case CertProvisioningWorkerState::kCanceled:
+      return true;
+    default:
+      return false;
+  }
+}
+
 //===================== CertProfile ============================================
 
 base::Optional<CertProfile> CertProfile::MakeFromValue(
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h
index 71a996e..c2762ce 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h
@@ -37,7 +37,8 @@
 // Numeric values are used in serialization and should not be remapped.
 enum class CertScope { kUser = 0, kDevice = 1, kMaxValue = kDevice };
 
-// These values are used in serialization and should be changed carefully.
+// These values are used in serialization and should be changed carefully. Also
+// enums.xml should be updated.
 enum class CertProvisioningWorkerState {
   kInitState = 0,
   kKeypairGenerated = 1,
@@ -47,13 +48,17 @@
   kKeypairMarked = 5,
   kSignCsrFinished = 6,
   kFinishCsrResponseReceived = 7,
-  kSucceed = 8,
+  kSucceeded = 8,
   kInconsistentDataError = 9,
   kFailed = 10,
   kCanceled = 11,
   kMaxValue = kCanceled,
 };
 
+// Returns true if the |state| is one of final states, i. e. worker should
+// finish its task in one of them.
+bool IsFinalState(CertProvisioningWorkerState state);
+
 using CertProfileId = std::string;
 
 // Names of CertProfile fields in a base::Value representation. Must be in sync
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.cc
new file mode 100644
index 0000000..ac1ef7b
--- /dev/null
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.cc
@@ -0,0 +1,75 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
+#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
+
+namespace chromeos {
+namespace cert_provisioning {
+
+#define CP_RESULT "ChromeOS.CertProvisioning.Result"
+#define CP_EVENT "ChromeOS.CertProvisioning.Event"
+#define CP_KEYPAIR_GENERATION_TIME \
+  "ChromeOS.CertProvisioning.KeypairGenerationTime"
+#define CP_VA_TIME "ChromeOS.CertProvisioning.VaTime"
+#define CP_CSR_SIGN_TIME "ChromeOS.CertProvisioning.CsrSignTime"
+
+#define CP_USER ".User"
+#define CP_DEVICE ".Device"
+
+namespace {
+// "*.User" should have index 0, "*.Device" should have index 1 (same as values
+// of CertScope).
+const char* const kResult[] = {CP_RESULT CP_USER, CP_RESULT CP_DEVICE};
+const char* const kEvent[] = {CP_EVENT CP_USER, CP_EVENT CP_DEVICE};
+const char* const kKeypairGenerationTime[] = {
+    CP_KEYPAIR_GENERATION_TIME CP_USER, CP_KEYPAIR_GENERATION_TIME CP_DEVICE};
+const char* const kVaTime[] = {CP_VA_TIME CP_USER, CP_VA_TIME CP_DEVICE};
+const char* const kSignCsrTime[] = {CP_CSR_SIGN_TIME CP_USER,
+                                    CP_CSR_SIGN_TIME CP_DEVICE};
+
+// CertScope has stable indexes because it is also used for serialization.
+constexpr int ToIdx(CertScope scope) {
+  static_assert(static_cast<int>(CertScope::kMaxValue) == 1,
+                "CertScope was modified, update arrays with metric names");
+  return static_cast<int>(scope);
+}
+}  // namespace
+
+void RecordResult(CertScope scope,
+                  CertProvisioningWorkerState final_state,
+                  CertProvisioningWorkerState prev_state) {
+  base::UmaHistogramEnumeration(kResult[ToIdx(scope)], final_state);
+  if (final_state == CertProvisioningWorkerState::kFailed) {
+    base::UmaHistogramEnumeration(kResult[ToIdx(scope)], prev_state);
+  }
+}
+
+void RecordEvent(CertScope scope, CertProvisioningEvent event) {
+  base::UmaHistogramEnumeration(kEvent[ToIdx(scope)], event);
+}
+
+void RecordKeypairGenerationTime(CertScope scope, base::TimeDelta sample) {
+  base::UmaHistogramCustomTimes(kKeypairGenerationTime[ToIdx(scope)], sample,
+                                base::TimeDelta::FromMilliseconds(1),
+                                base::TimeDelta::FromMinutes(2), 25);
+}
+
+void RecordVerifiedAccessTime(CertScope scope, base::TimeDelta sample) {
+  base::UmaHistogramCustomTimes(kVaTime[ToIdx(scope)], sample,
+                                base::TimeDelta::FromMilliseconds(1),
+                                base::TimeDelta::FromMinutes(2), 25);
+}
+
+void RecordCsrSignTime(CertScope scope, base::TimeDelta sample) {
+  base::UmaHistogramCustomTimes(kSignCsrTime[ToIdx(scope)], sample,
+                                base::TimeDelta::FromMilliseconds(1),
+                                base::TimeDelta::FromMinutes(2), 25);
+}
+
+}  // namespace cert_provisioning
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.h b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.h
new file mode 100644
index 0000000..20f8cf3
--- /dev/null
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.h
@@ -0,0 +1,56 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_CERT_PROVISIONING_CERT_PROVISIONING_METRICS_H_
+#define CHROME_BROWSER_CHROMEOS_CERT_PROVISIONING_CERT_PROVISIONING_METRICS_H_
+
+#include "base/time/time.h"
+#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
+
+namespace chromeos {
+namespace cert_provisioning {
+
+// The enum is used for UMA, the values should not be renumerated.
+enum class CertProvisioningEvent {
+  // Some worker tried to register(or reregister) for invalidation topic.
+  kRegisteredToInvalidationTopic = 0,
+  // Invalidation received.
+  kInvalidationReceived = 1,
+  // Some worker retried to continue without invalidation.
+  kWorkerRetryWithoutInvalidation = 2,
+  // Some worker retried to continue without invalidation and made some
+  // progress.
+  kWorkerRetrySucceededWithoutInvalidation = 3,
+  // Profile retried manually from UI.
+  kWorkerRetryManual = 4,
+  kWorkerCreated = 5,
+  kWorkerDeserialized = 6,
+  kWorkerDeserializationFailed = 7,
+  kMaxValue = kWorkerDeserializationFailed
+};
+
+// Records the |final_state| of a worker. If the worker is failed, also records
+// its |prev_state| into the same histogram. It is reasonable to put both of
+// them in the same histogram because the worker should never stop on an
+// intermediate state and even if it does, it is the same as failure.
+void RecordResult(CertScope scope,
+                  CertProvisioningWorkerState final_state,
+                  CertProvisioningWorkerState prev_state);
+
+void RecordEvent(CertScope scope, CertProvisioningEvent event);
+
+// Records time of generation key pair by certificate provisioning worker.
+void RecordKeypairGenerationTime(CertScope scope, base::TimeDelta sample);
+
+// Records time of building Verified Access response by certificate provisioning
+// worker.
+void RecordVerifiedAccessTime(CertScope scope, base::TimeDelta sample);
+
+// Records time of signing a CSR by certificate provisioning worker.
+void RecordCsrSignTime(CertScope scope, base::TimeDelta sample);
+
+}  // namespace cert_provisioning
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_CERT_PROVISIONING_CERT_PROVISIONING_METRICS_H_
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc
index 3c8db9b5..ccd640a 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
+#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
@@ -25,6 +26,7 @@
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state_handler.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
 
 namespace chromeos {
 namespace cert_provisioning {
@@ -169,19 +171,19 @@
 }
 
 CertProvisioningScheduler::~CertProvisioningScheduler() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   network_state_handler_->RemoveObserver(this, FROM_HERE);
 }
 
 void CertProvisioningScheduler::ScheduleInitialUpdate() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::Bind(&CertProvisioningScheduler::InitialUpdateCerts,
                             weak_factory_.GetWeakPtr()));
 }
 
 void CertProvisioningScheduler::ScheduleDailyUpdate() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::Bind(&CertProvisioningScheduler::DailyUpdateCerts,
@@ -190,7 +192,7 @@
 }
 
 void CertProvisioningScheduler::ScheduleRetry(const CertProfile& profile) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::Bind(&CertProvisioningScheduler::ProcessProfile,
@@ -199,13 +201,13 @@
 }
 
 void CertProvisioningScheduler::InitialUpdateCerts() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   DeleteCertsWithoutPolicy();
 }
 
 void CertProvisioningScheduler::DeleteCertsWithoutPolicy() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   std::vector<CertProfile> profiles = GetCertProfiles();
   std::set<std::string> cert_profile_ids_to_keep;
@@ -222,7 +224,7 @@
 
 void CertProvisioningScheduler::OnDeleteCertsWithoutPolicyDone(
     const std::string& error_message) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   cert_deleter_.reset();
 
@@ -237,7 +239,7 @@
 }
 
 void CertProvisioningScheduler::CleanVaKeysIfIdle() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   if (!workers_.empty()) {
     OnCleanVaKeysIfIdleDone(true);
@@ -252,7 +254,7 @@
 
 void CertProvisioningScheduler::OnCleanVaKeysIfIdleDone(
     base::Optional<bool> delete_result) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   if (!delete_result.has_value() || !delete_result.value()) {
     LOG(ERROR) << "Failed to delete keys while idle";
@@ -263,7 +265,7 @@
 }
 
 void CertProvisioningScheduler::RegisterForPrefsChanges() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   pref_change_registrar_.Init(pref_service_);
   pref_change_registrar_.Add(
@@ -272,7 +274,7 @@
 }
 
 void CertProvisioningScheduler::DailyUpdateCerts() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   failed_cert_profiles_.clear();
   UpdateCerts();
@@ -280,7 +282,7 @@
 }
 
 void CertProvisioningScheduler::DeserializeWorkers() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   const base::Value* saved_workers =
       pref_service_->Get(GetPrefNameForSerialization(cert_scope_));
@@ -307,13 +309,16 @@
 }
 
 void CertProvisioningScheduler::OnPrefsChange() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   UpdateCerts();
 }
 
 void CertProvisioningScheduler::UpdateOneCert(
     const std::string& cert_profile_id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  RecordEvent(cert_scope_, CertProvisioningEvent::kWorkerRetryManual);
+
   if (!CheckInternetConnection()) {
     return;
   }
@@ -327,7 +332,7 @@
 }
 
 void CertProvisioningScheduler::UpdateCerts() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   if (!CheckInternetConnection()) {
     return;
@@ -351,7 +356,7 @@
     std::map<std::string, scoped_refptr<net::X509Certificate>>
         existing_certs_with_ids,
     const std::string& error_message) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   certs_with_ids_getter_.reset();
 
@@ -362,7 +367,7 @@
 
   std::vector<CertProfile> profiles = GetCertProfiles();
 
-  DeleteWorkersWithoutPolicy(profiles);
+  CancelWorkersWithoutPolicy(profiles);
 
   for (const auto& profile : profiles) {
     if (base::Contains(existing_certs_with_ids, profile.profile_id) ||
@@ -376,17 +381,23 @@
 
 void CertProvisioningScheduler::ProcessProfile(
     const CertProfile& cert_profile) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   CertProvisioningWorker* worker = FindWorker(cert_profile.profile_id);
-  if (!worker || (worker->GetCertProfile().policy_version !=
-                  cert_profile.policy_version)) {
-    EraseKey(failed_cert_profiles_, cert_profile.profile_id);
-    // Create new worker or replace an existing one.
+  if (!worker) {
     CreateCertProvisioningWorker(cert_profile);
     return;
   }
 
+  if ((worker->GetCertProfile().policy_version !=
+       cert_profile.policy_version)) {
+    // The worker has outdated policy version. Make it stop, clean up current
+    // state and report back through its callback. That will trigger retry for
+    // its certificate profile.
+    worker->Stop(CertProvisioningWorkerState::kInconsistentDataError);
+    return;
+  }
+
   if (worker->IsWaiting()) {
     worker->DoStep();
     return;
@@ -398,7 +409,7 @@
 
 void CertProvisioningScheduler::CreateCertProvisioningWorker(
     CertProfile cert_profile) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   std::unique_ptr<CertProvisioningWorker> worker =
       CertProvisioningWorkerFactory::Get()->Create(
@@ -414,7 +425,7 @@
 void CertProvisioningScheduler::OnProfileFinished(
     const CertProfile& profile,
     CertProvisioningWorkerState state) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   auto worker_iter = workers_.find(profile.profile_id);
   if (worker_iter == workers_.end()) {
@@ -424,7 +435,7 @@
   }
 
   switch (state) {
-    case CertProvisioningWorkerState::kSucceed:
+    case CertProvisioningWorkerState::kSucceeded:
       VLOG(0) << "Successfully provisioned certificate for profile: "
               << profile.profile_id;
       break;
@@ -433,6 +444,8 @@
                    << profile.profile_id;
       ScheduleRetry(profile);
       break;
+    case CertProvisioningWorkerState::kCanceled:
+      break;
     default:
       LOG(ERROR) << "Failed to process certificate profile: "
                  << profile.profile_id;
@@ -445,7 +458,7 @@
 
 CertProvisioningWorker* CertProvisioningScheduler::FindWorker(
     CertProfileId profile_id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   auto iter = workers_.find(profile_id);
   if (iter == workers_.end()) {
@@ -457,7 +470,7 @@
 
 base::Optional<CertProfile> CertProvisioningScheduler::GetOneCertProfile(
     const std::string& cert_profile_id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   const base::Value* profile_list = pref_service_->Get(pref_name_);
   if (!profile_list) {
@@ -478,7 +491,7 @@
 }
 
 std::vector<CertProfile> CertProvisioningScheduler::GetCertProfiles() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   const base::Value* profile_list = pref_service_->Get(pref_name_);
   if (!profile_list) {
@@ -501,18 +514,18 @@
 }
 
 const WorkerMap& CertProvisioningScheduler::GetWorkers() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   return workers_;
 }
 
 const std::map<std::string, FailedWorkerInfo>&
 CertProvisioningScheduler::GetFailedCertProfileIds() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   return failed_cert_profiles_;
 }
 
 bool CertProvisioningScheduler::CheckInternetConnection() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   const NetworkState* network = network_state_handler_->DefaultNetwork();
   bool is_online = network && network->IsOnline();
   is_waiting_for_online_ = !is_online;
@@ -521,7 +534,7 @@
 
 void CertProvisioningScheduler::OnNetworkChange(
     const chromeos::NetworkState* network) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (is_waiting_for_online_ && network && network->IsOnline()) {
     UpdateCerts();
   }
@@ -529,19 +542,19 @@
 
 void CertProvisioningScheduler::DefaultNetworkChanged(
     const chromeos::NetworkState* network) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   OnNetworkChange(network);
 }
 
 void CertProvisioningScheduler::NetworkConnectionStateChanged(
     const chromeos::NetworkState* network) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   OnNetworkChange(network);
 }
 
 void CertProvisioningScheduler::UpdateFailedCertProfiles(
     const CertProvisioningWorker& worker) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   FailedWorkerInfo info;
   info.state = worker.GetPreviousState();
   info.public_key = worker.GetPublicKey();
@@ -549,9 +562,9 @@
   failed_cert_profiles_[worker.GetCertProfile().profile_id] = std::move(info);
 }
 
-void CertProvisioningScheduler::DeleteWorkersWithoutPolicy(
+void CertProvisioningScheduler::CancelWorkersWithoutPolicy(
     const std::vector<CertProfile>& profiles) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   if (workers_.empty()) {
     return;
@@ -562,14 +575,13 @@
     cert_profile_ids.insert(profile.profile_id);
   }
 
-  for (auto iter = workers_.begin(); iter != workers_.end();) {
-    const auto& worker = iter->second;
-    if (cert_profile_ids.find(worker->GetCertProfile().profile_id) ==
+  for (auto& kv : workers_) {
+    auto& worker_ptr = kv.second;
+    if (cert_profile_ids.find(worker_ptr->GetCertProfile().profile_id) ==
         cert_profile_ids.end()) {
-      worker->Cancel();
-      iter = workers_.erase(iter);
-    } else {
-      ++iter;
+      // This will trigger clean up (if any) in the worker and make it call its
+      // callback.
+      worker_ptr->Stop(CertProvisioningWorkerState::kCanceled);
     }
   }
 }
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h
index e87d5d1d..4a95879f 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h
@@ -46,6 +46,9 @@
 // This class is a part of certificate provisioning feature. It tracks updates
 // of |RequiredClientCertificateForUser|, |RequiredClientCertificateForDevice|
 // policies and creates one CertProvisioningWorker for every policy entry.
+// Should work on the UI thread because it interacts with PlatformKeysService
+// and some methods are called from the UI to populate certificate manager
+// settings page.
 class CertProvisioningScheduler : public NetworkStateHandlerObserver {
  public:
   static std::unique_ptr<CertProvisioningScheduler>
@@ -68,7 +71,8 @@
   CertProvisioningScheduler(const CertProvisioningScheduler&) = delete;
   CertProvisioningScheduler& operator=(const CertProvisioningScheduler&) =
       delete;
-
+  // Intended to be called when a user press a button in certificate manager UI.
+  // Retries provisioning of a specific certificate.
   void UpdateOneCert(const std::string& cert_profile_id);
   void UpdateCerts();
   void OnProfileFinished(const CertProfile& profile,
@@ -87,7 +91,7 @@
   void InitialUpdateCerts();
   void DeleteCertsWithoutPolicy();
   void OnDeleteCertsWithoutPolicyDone(const std::string& error_message);
-  void DeleteWorkersWithoutPolicy(const std::vector<CertProfile>& profiles);
+  void CancelWorkersWithoutPolicy(const std::vector<CertProfile>& profiles);
   void CleanVaKeysIfIdle();
   void OnCleanVaKeysIfIdleDone(base::Optional<bool> delete_result);
   void RegisterForPrefsChanges();
@@ -144,7 +148,6 @@
   std::unique_ptr<CertProvisioningCertDeleter> cert_deleter_;
   std::unique_ptr<CertProvisioningInvalidatorFactory> invalidator_factory_;
 
-  SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<CertProvisioningScheduler> weak_factory_{this};
 };
 
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_unittest.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_unittest.cc
index 5b3e899..912c2e4 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_unittest.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_unittest.cc
@@ -242,7 +242,7 @@
 
   // Emulate callback from the worker.
   scheduler.OnProfileFinished(cert_profile,
-                              CertProvisioningWorkerState::kSucceed);
+                              CertProvisioningWorkerState::kSucceeded);
 
   // Finished worker should be deleted.
   EXPECT_EQ(scheduler.GetWorkers().size(), 0U);
@@ -392,7 +392,7 @@
   ASSERT_EQ(scheduler.GetWorkers().size(), 1U);
 
   // Emulate callback from the worker.
-  scheduler.OnProfileFinished(profile, CertProvisioningWorkerState::kSucceed);
+  scheduler.OnProfileFinished(profile, CertProvisioningWorkerState::kSucceeded);
 
   ASSERT_EQ(scheduler.GetWorkers().size(), 0U);
   EXPECT_TRUE(scheduler.GetFailedCertProfileIds().empty());
@@ -471,7 +471,7 @@
 
   // worker0 successfully finished. Should be just deleted.
   scheduler.OnProfileFinished(cert_profile0,
-                              CertProvisioningWorkerState::kSucceed);
+                              CertProvisioningWorkerState::kSucceeded);
 
   // worker1 is waiting. Should be continued.
   worker1->SetExpectations(/*do_step_times=*/AtLeast(1), /*is_waiting=*/true,
@@ -605,7 +605,6 @@
   const char kCertProfileId[] = "cert_profile_id_1";
   const char kCertProfileVersion1[] = "cert_profile_version_1";
   const char kCertProfileVersion2[] = "cert_profile_version_2";
-  const char kCertProfileVersion3[] = "cert_profile_version_3";
 
   CertProvisioningScheduler scheduler(
       cert_scope, GetProfile(), &pref_service_,
@@ -689,12 +688,6 @@
   scheduler.UpdateCerts();
   EXPECT_EQ(scheduler.GetWorkers().size(), 1U);
 
-  // Add a new worker to the factory.
-  CertProfile cert_profile_v3{kCertProfileId, kCertProfileVersion3};
-  worker = mock_factory_.ExpectCreateReturnMock(cert_scope, cert_profile_v3);
-  worker->SetExpectations(/*do_step_times=*/AtLeast(1), /*is_waiting=*/false,
-                          cert_profile_v3);
-
   // On policy update if existing profile has changed its policy_version,
   // scheduler should recreate the worker for it.
   config = ParseJson(
@@ -703,8 +696,20 @@
            "policy_version":"cert_profile_version_3",
            "key_algorithm":"rsa",
            "renewal_period_seconds": 365000}])");
+
+  // On policy change scheduler should detect mismatch in policy versions and
+  // stop the worker.
+  EXPECT_CALL(*worker,
+              Stop(CertProvisioningWorkerState::kInconsistentDataError));
+
   pref_service_.Set(prefs::kRequiredClientCertificateForDevice, config);
-  EXPECT_EQ(scheduler.GetWorkers().size(), 1U);
+  // EXPECT_EQ(scheduler.GetWorkers().size(), 1U);
+
+  // Emulate that after some time the worker reports back to scheduler.
+  FastForwardBy(base::TimeDelta::FromSeconds(10));
+  scheduler.OnProfileFinished(
+      cert_profile_v1, CertProvisioningWorkerState::kInconsistentDataError);
+  EXPECT_EQ(scheduler.GetWorkers().size(), 0U);
 }
 
 TEST_F(CertProvisioningSchedulerTest, RetryAfterNoInternetConnection) {
@@ -791,11 +796,16 @@
   FastForwardBy(base::TimeDelta::FromSeconds(1));
   EXPECT_EQ(scheduler.GetWorkers().size(), 1U);
 
-  EXPECT_CALL(*worker, Cancel);
+  EXPECT_CALL(*worker, Stop(CertProvisioningWorkerState::kCanceled));
 
   config = ParseJson("[]");
   pref_service_.Set(prefs::kRequiredClientCertificateForDevice, config);
 
+  FastForwardBy(base::TimeDelta::FromSeconds(1));
+  // Emulate callback from the worker.
+  scheduler.OnProfileFinished(cert_profile,
+                              CertProvisioningWorkerState::kCanceled);
+
   ASSERT_EQ(scheduler.GetWorkers().size(), 0U);
 }
 
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc
index 89ef6a1..58ba7df 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc
@@ -8,9 +8,11 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/no_destructor.h"
+#include "base/time/time.h"
 #include "chrome/browser/chromeos/attestation/tpm_challenge_key_result.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_invalidator.h"
+#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_serializer.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
@@ -94,7 +96,7 @@
     case CertProvisioningWorkerState::kFinishCsrResponseReceived:
       res -= 1;
       FALLTHROUGH;
-    case CertProvisioningWorkerState::kSucceed:
+    case CertProvisioningWorkerState::kSucceeded:
     case CertProvisioningWorkerState::kInconsistentDataError:
     case CertProvisioningWorkerState::kFailed:
     case CertProvisioningWorkerState::kCanceled:
@@ -128,6 +130,7 @@
     policy::CloudPolicyClient* cloud_policy_client,
     std::unique_ptr<CertProvisioningInvalidator> invalidator,
     CertProvisioningWorkerCallback callback) {
+  RecordEvent(cert_scope, CertProvisioningEvent::kWorkerCreated);
   return std::make_unique<CertProvisioningWorkerImpl>(
       cert_scope, profile, pref_service, cert_profile, cloud_policy_client,
       std::move(invalidator), std::move(callback));
@@ -147,8 +150,11 @@
       std::move(invalidator), std::move(callback));
   if (!CertProvisioningSerializer::DeserializeWorker(saved_worker,
                                                      worker.get())) {
+    RecordEvent(cert_scope,
+                CertProvisioningEvent::kWorkerDeserializationFailed);
     return {};
   }
+  RecordEvent(cert_scope, CertProvisioningEvent::kWorkerDeserialized);
   return worker;
 }
 
@@ -219,7 +225,7 @@
     case CertProvisioningWorkerState::kFinishCsrResponseReceived:
       DownloadCert();
       return;
-    case CertProvisioningWorkerState::kSucceed:
+    case CertProvisioningWorkerState::kSucceeded:
     case CertProvisioningWorkerState::kInconsistentDataError:
     case CertProvisioningWorkerState::kFailed:
     case CertProvisioningWorkerState::kCanceled:
@@ -229,9 +235,12 @@
   NOTREACHED() << " " << static_cast<uint>(state_);
 }
 
-void CertProvisioningWorkerImpl::Cancel() {
+void CertProvisioningWorkerImpl::Stop(CertProvisioningWorkerState state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  UpdateState(CertProvisioningWorkerState::kCanceled);
+  DCHECK(IsFinalState(state));
+
+  CancelScheduledTasks();
+  UpdateState(state);
 }
 
 void CertProvisioningWorkerImpl::UpdateState(
@@ -242,10 +251,17 @@
   prev_state_ = state_;
   state_ = new_state;
 
+  if (is_continued_without_invalidation_for_uma_) {
+    RecordEvent(
+        cert_scope_,
+        CertProvisioningEvent::kWorkerRetrySucceededWithoutInvalidation);
+    is_continued_without_invalidation_for_uma_ = false;
+  }
+
   HandleSerialization();
 
-  if (IsFinished()) {
-    CleanUpAndMaybeRunCallback();
+  if (IsFinalState(state_)) {
+    CleanUpAndRunCallback();
   }
 }
 
@@ -259,13 +275,16 @@
       GetVaKeyName(cert_scope_, cert_profile_.profile_id), profile_,
       GetVaKeyNameForSpkac(cert_scope_, cert_profile_.profile_id),
       base::BindOnce(&CertProvisioningWorkerImpl::OnGenerateKeyDone,
-                     weak_factory_.GetWeakPtr()));
+                     weak_factory_.GetWeakPtr(), base::TimeTicks::Now()));
 }
 
 void CertProvisioningWorkerImpl::OnGenerateKeyDone(
+    base::TimeTicks start_time,
     const attestation::TpmChallengeKeyResult& result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  RecordKeypairGenerationTime(cert_scope_, base::TimeTicks::Now() - start_time);
+
   if (result.result_code ==
       attestation::TpmChallengeKeyResultCode::kGetCertificateFailedError) {
     LOG(WARNING) << "Failed to get certificate for a key";
@@ -339,13 +358,16 @@
       va_challenge_, /*include_signed_public_key=*/true,
       base::BindOnce(
           &CertProvisioningWorkerImpl::OnBuildVaChallengeResponseDone,
-          weak_factory_.GetWeakPtr()));
+          weak_factory_.GetWeakPtr(), base::TimeTicks::Now()));
 }
 
 void CertProvisioningWorkerImpl::OnBuildVaChallengeResponseDone(
+    base::TimeTicks start_time,
     const attestation::TpmChallengeKeyResult& result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  RecordVerifiedAccessTime(cert_scope_, base::TimeTicks::Now() - start_time);
+
   if (!result.IsSuccess()) {
     LOG(ERROR) << "Failed to build challenge response: "
                << result.GetErrorMessage();
@@ -425,14 +447,17 @@
       GetPlatformKeysTokenId(cert_scope_), csr_, public_key_,
       hashing_algorithm_.value(),
       base::BindRepeating(&CertProvisioningWorkerImpl::OnSignCsrDone,
-                          weak_factory_.GetWeakPtr()));
+                          weak_factory_.GetWeakPtr(), base::TimeTicks::Now()));
 }
 
 void CertProvisioningWorkerImpl::OnSignCsrDone(
+    base::TimeTicks start_time,
     const std::string& signature,
     const std::string& error_message) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  RecordCsrSignTime(cert_scope_, base::TimeTicks::Now() - start_time);
+
   if (!error_message.empty()) {
     LOG(ERROR) << "Failed to sign CSR: " << error_message;
     UpdateState(CertProvisioningWorkerState::kFailed);
@@ -521,21 +546,7 @@
     return;
   }
 
-  UpdateState(CertProvisioningWorkerState::kSucceed);
-}
-
-bool CertProvisioningWorkerImpl::IsFinished() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  switch (state_) {
-    case CertProvisioningWorkerState::kSucceed:
-    case CertProvisioningWorkerState::kInconsistentDataError:
-    case CertProvisioningWorkerState::kFailed:
-    case CertProvisioningWorkerState::kCanceled:
-      return true;
-    default:
-      return false;
-  }
+  UpdateState(CertProvisioningWorkerState::kSucceeded);
 }
 
 bool CertProvisioningWorkerImpl::IsWaiting() const {
@@ -617,19 +628,32 @@
   base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&CertProvisioningWorkerImpl::OnShouldContinue,
-                     weak_factory_.GetWeakPtr()),
+                     weak_factory_.GetWeakPtr(), ContinueReason::kTimeout),
       delay);
 
   is_waiting_ = true;
   VLOG(0) << "Next step scheduled in " << delay;
 }
 
-void CertProvisioningWorkerImpl::OnShouldContinue() {
+void CertProvisioningWorkerImpl::OnShouldContinue(ContinueReason reason) {
+  switch (reason) {
+    case ContinueReason::kInvalidation:
+      RecordEvent(cert_scope_, CertProvisioningEvent::kInvalidationReceived);
+      break;
+    case ContinueReason::kTimeout:
+      RecordEvent(cert_scope_,
+                  CertProvisioningEvent::kWorkerRetryWithoutInvalidation);
+      break;
+  }
+
   // Worker is already doing something.
   if (!IsWaiting()) {
     return;
   }
 
+  is_continued_without_invalidation_for_uma_ =
+      (reason == ContinueReason::kTimeout);
+
   DoStep();
 }
 
@@ -638,13 +662,13 @@
   weak_factory_.InvalidateWeakPtrs();
 }
 
-void CertProvisioningWorkerImpl::CleanUpAndMaybeRunCallback() {
+void CertProvisioningWorkerImpl::CleanUpAndRunCallback() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   UnregisterFromInvalidationTopic();
 
   // Keep conditions mutually exclusive.
-  if (state_ == CertProvisioningWorkerState::kSucceed) {
+  if (state_ == CertProvisioningWorkerState::kSucceeded) {
     // No extra clean up is necessary.
     OnCleanUpDone();
     return;
@@ -695,15 +719,13 @@
   if (!error_message.empty()) {
     LOG(ERROR) << "Failed to delete a key: " << error_message;
   }
+
   OnCleanUpDone();
 }
 
 void CertProvisioningWorkerImpl::OnCleanUpDone() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (state_ == CertProvisioningWorkerState::kCanceled) {
-    return;
-  }
+  RecordResult(cert_scope_, state_, prev_state_);
   std::move(callback_).Run(cert_profile_, state_);
 }
 
@@ -728,7 +750,7 @@
     case CertProvisioningWorkerState::kFinishCsrResponseReceived:
       CertProvisioningSerializer::SerializeWorkerToPrefs(pref_service_, *this);
       break;
-    case CertProvisioningWorkerState::kSucceed:
+    case CertProvisioningWorkerState::kSucceeded:
     case CertProvisioningWorkerState::kInconsistentDataError:
     case CertProvisioningWorkerState::kFailed:
     case CertProvisioningWorkerState::kCanceled:
@@ -762,7 +784,11 @@
   invalidator_->Register(
       invalidation_topic_,
       base::BindRepeating(&CertProvisioningWorkerImpl::OnShouldContinue,
-                          weak_factory_.GetWeakPtr()));
+                          weak_factory_.GetWeakPtr(),
+                          ContinueReason::kInvalidation));
+
+  RecordEvent(cert_scope_,
+              CertProvisioningEvent::kRegisteredToInvalidationTopic);
 }
 
 void CertProvisioningWorkerImpl::UnregisterFromInvalidationTopic() {
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h
index f40499c..c1d20cf 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h
@@ -75,9 +75,10 @@
 
   // Continue provisioning a certificate.
   virtual void DoStep() = 0;
-  // Stops the worker, triggers clean ups (deletes serialized state, keys, and
-  // so on), Canceled workers will never call the callback.
-  virtual void Cancel() = 0;
+  // Sets worker's state to one of final ones. That triggers corresponding
+  // clean ups (deletes serialized state, keys, and so on) and returns |state|
+  // via callback.
+  virtual void Stop(CertProvisioningWorkerState state) = 0;
   // Returns true, if the worker is waiting for some future event. |DoStep| can
   // be called to try continue right now.
   virtual bool IsWaiting() const = 0;
@@ -106,7 +107,7 @@
 
   // CertProvisioningWorker
   void DoStep() override;
-  void Cancel() override;
+  void Stop(CertProvisioningWorkerState state) override;
   bool IsWaiting() const override;
   const CertProfile& GetCertProfile() const override;
   const std::string& GetPublicKey() const override;
@@ -117,7 +118,8 @@
   friend class CertProvisioningSerializer;
 
   void GenerateKey();
-  void OnGenerateKeyDone(const attestation::TpmChallengeKeyResult& result);
+  void OnGenerateKeyDone(base::TimeTicks start_time,
+                         const attestation::TpmChallengeKeyResult& result);
 
   void StartCsr();
   void OnStartCsrDone(policy::DeviceManagementStatus status,
@@ -130,6 +132,7 @@
 
   void BuildVaChallengeResponse();
   void OnBuildVaChallengeResponseDone(
+      base::TimeTicks start_time,
       const attestation::TpmChallengeKeyResult& result);
 
   void RegisterKey();
@@ -139,7 +142,8 @@
   void OnMarkKeyDone(const std::string& error_message);
 
   void SignCsr();
-  void OnSignCsrDone(const std::string& signature,
+  void OnSignCsrDone(base::TimeTicks start_time,
+                     const std::string& signature,
                      const std::string& error_message);
 
   void FinishCsr();
@@ -160,7 +164,8 @@
   void ScheduleNextStep(base::TimeDelta delay);
   void CancelScheduledTasks();
 
-  void OnShouldContinue();
+  enum class ContinueReason { kTimeout, kInvalidation };
+  void OnShouldContinue(ContinueReason reason);
 
   // Registers for |invalidation_topic_| that allows to receive notification
   // when server side is ready to continue provisioning process.
@@ -174,7 +179,6 @@
   // worker can be destroyed in callback and should not use any member fields
   // after that.
   void UpdateState(CertProvisioningWorkerState state);
-  bool IsFinished() const;
 
   // Serializes the worker or deletes serialized state accroding to the current
   // state. Some states are considered unrecoverable, some can be reached again
@@ -184,7 +188,7 @@
   // to be called from CertProvisioningDeserializer.
   void InitAfterDeserialization();
 
-  void CleanUpAndMaybeRunCallback();
+  void CleanUpAndRunCallback();
   void OnDeleteVaKeyDone(base::Optional<bool> delete_result);
   void OnRemoveKeyDone(const std::string& error_message);
   void OnCleanUpDone();
@@ -208,6 +212,9 @@
   // on failure.
   CertProvisioningWorkerState prev_state_ = state_;
   bool is_waiting_ = false;
+  // Used for an UMA metric to track situation when the worker did not receive
+  // an invalidation for a completed server side task.
+  bool is_continued_without_invalidation_for_uma_ = false;
   // Calculates retry timeout for network related failures.
   net::BackoffEntry request_backoff_;
 
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc
index 50688f2..25a324f 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc
@@ -8,11 +8,13 @@
 #include "base/json/json_string_value_serializer.h"
 #include "base/json/json_writer.h"
 #include "base/test/gmock_callback_support.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/values_test_util.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/attestation/mock_tpm_challenge_key_subtle.h"
 #include "chrome/browser/chromeos/attestation/tpm_challenge_key_subtle.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
+#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.h"
 #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_test_helpers.h"
 #include "chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_invalidator.h"
 #include "chrome/browser/chromeos/platform_keys/mock_platform_keys_service.h"
@@ -378,6 +380,8 @@
 // Checks that the worker makes all necessary requests to other modules during
 // success scenario.
 TEST_F(CertProvisioningWorkerTest, Success) {
+  base::HistogramTester histogram_tester;
+
   CertProfile cert_profile{kCertProfileId, kCertProfileVersion};
 
   MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
@@ -432,11 +436,23 @@
     EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);
 
     EXPECT_CALL(callback_observer_,
-                Callback(cert_profile, CertProvisioningWorkerState::kSucceed))
+                Callback(cert_profile, CertProvisioningWorkerState::kSucceeded))
         .Times(1);
   }
 
   worker.DoStep();
+
+  histogram_tester.ExpectUniqueSample("ChromeOS.CertProvisioning.Result.User",
+                                      CertProvisioningWorkerState::kSucceeded,
+                                      1);
+  histogram_tester.ExpectUniqueSample(
+      "ChromeOS.CertProvisioning.Event.User",
+      CertProvisioningEvent::kRegisteredToInvalidationTopic, 1);
+  histogram_tester.ExpectTotalCount(
+      "ChromeOS.CertProvisioning.KeypairGenerationTime.User", 1);
+  histogram_tester.ExpectTotalCount("ChromeOS.CertProvisioning.VaTime.User", 1);
+  histogram_tester.ExpectTotalCount(
+      "ChromeOS.CertProvisioning.CsrSignTime.User", 1);
 }
 
 // Checks that the worker makes all necessary requests to other modules during
@@ -487,7 +503,7 @@
         platform_keys::kTokenIdUser, /*certificate=*/_, /*callback=*/_));
 
     EXPECT_CALL(callback_observer_,
-                Callback(cert_profile, CertProvisioningWorkerState::kSucceed))
+                Callback(cert_profile, CertProvisioningWorkerState::kSucceeded))
         .Times(1);
   }
 
@@ -586,11 +602,11 @@
         platform_keys::kTokenIdSystem, /*certificate=*/_, /*callback=*/_));
 
     EXPECT_CALL(callback_observer_,
-                Callback(cert_profile, CertProvisioningWorkerState::kSucceed))
+                Callback(cert_profile, CertProvisioningWorkerState::kSucceeded))
         .Times(1);
 
     worker.DoStep();
-    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceed);
+    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);
   }
 }
 
@@ -697,10 +713,10 @@
               CertProvisioningWorkerState::kFinishCsrResponseReceived);
 
     EXPECT_CALL(callback_observer_,
-                Callback(cert_profile, CertProvisioningWorkerState::kSucceed))
+                Callback(cert_profile, CertProvisioningWorkerState::kSucceeded))
         .Times(1);
     FastForwardBy(download_cert_real_delay + small_delay);
-    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceed);
+    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);
   }
 }
 
@@ -746,6 +762,8 @@
 // Checks that when the server returns response error, the worker will enter an
 // error state and stop the provisioning. Also check factory.
 TEST_F(CertProvisioningWorkerTest, ResponseErrorHandling) {
+  base::HistogramTester histogram_tester;
+
   CertProfile cert_profile{kCertProfileId, kCertProfileVersion};
 
   MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
@@ -778,6 +796,13 @@
 
   worker->DoStep();
   FastForwardBy(TimeDelta::FromSeconds(1));
+
+  histogram_tester.ExpectBucketCount("ChromeOS.CertProvisioning.Result.User",
+                                     CertProvisioningWorkerState::kFailed, 1);
+  histogram_tester.ExpectBucketCount(
+      "ChromeOS.CertProvisioning.Result.User",
+      CertProvisioningWorkerState::kKeypairGenerated, 1);
+  histogram_tester.ExpectTotalCount("ChromeOS.CertProvisioning.Result.User", 2);
 }
 
 TEST_F(CertProvisioningWorkerTest, InconsistentDataErrorHandling) {
@@ -879,6 +904,8 @@
 // Checks that the worker removes a key when an error occurs after the key was
 // registered.
 TEST_F(CertProvisioningWorkerTest, RemoveRegisteredKey) {
+  base::HistogramTester histogram_tester;
+
   CertProfile cert_profile{kCertProfileId, kCertProfileVersion};
   MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
   MockCertProvisioningInvalidator* mock_invalidator = nullptr;
@@ -929,6 +956,13 @@
 
   worker.DoStep();
   FastForwardBy(TimeDelta::FromSeconds(1));
+
+  histogram_tester.ExpectBucketCount("ChromeOS.CertProvisioning.Result.User",
+                                     CertProvisioningWorkerState::kFailed, 1);
+  histogram_tester.ExpectBucketCount(
+      "ChromeOS.CertProvisioning.Result.User",
+      CertProvisioningWorkerState::kKeyRegistered, 1);
+  histogram_tester.ExpectTotalCount("ChromeOS.CertProvisioning.Result.User", 2);
 }
 
 class PrefServiceObserver {
@@ -1116,7 +1150,7 @@
     EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);
 
     EXPECT_CALL(callback_observer_,
-                Callback(cert_profile, CertProvisioningWorkerState::kSucceed))
+                Callback(cert_profile, CertProvisioningWorkerState::kSucceeded))
         .Times(1);
     worker->DoStep();
   }
@@ -1230,6 +1264,8 @@
 }
 
 TEST_F(CertProvisioningWorkerTest, CancelDeviceWorker) {
+  base::HistogramTester histogram_tester;
+
   CertScope cert_scope = CertScope::kDevice;
   CertProfile cert_profile{kCertProfileId, kCertProfileVersion};
 
@@ -1284,8 +1320,17 @@
     pref_val = ParseJson("{}");
     EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);
 
-    worker->Cancel();
+    worker->Stop(CertProvisioningWorkerState::kCanceled);
+
+    EXPECT_CALL(callback_observer_,
+                Callback(cert_profile, CertProvisioningWorkerState::kCanceled))
+        .Times(1);
+    FastForwardBy(TimeDelta::FromSeconds(1));
   }
+
+  histogram_tester.ExpectUniqueSample("ChromeOS.CertProvisioning.Result.Device",
+                                      CertProvisioningWorkerState::kCanceled,
+                                      1);
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_worker.h b/chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_worker.h
index 95f43ea4..907291b 100644
--- a/chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_worker.h
+++ b/chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_worker.h
@@ -64,7 +64,7 @@
   ~MockCertProvisioningWorker() override;
 
   MOCK_METHOD(void, DoStep, (), (override));
-  MOCK_METHOD(void, Cancel, (), (override));
+  MOCK_METHOD(void, Stop, (CertProvisioningWorkerState), (override));
   MOCK_METHOD(bool, IsWaiting, (), (const override));
   MOCK_METHOD(const CertProfile&, GetCertProfile, (), (const override));
   MOCK_METHOD(const std::string&, GetPublicKey, (), (const override));
diff --git a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
index e915f9e..d1e3857e 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
@@ -112,6 +112,10 @@
   RunTestURL("foreground/js/task_controller_unittest_gen.html");
 }
 
+IN_PROC_BROWSER_TEST_F(FileManagerJsTest, FileManagerCommandsTest) {
+  RunTestURL("foreground/js/file_manager_commands_unittest_gen.html");
+}
+
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, FileTasks) {
   RunTestURL("foreground/js/file_tasks_unittest_gen.html");
 }
diff --git a/chrome/browser/chromeos/login/kiosk_browsertest.cc b/chrome/browser/chromeos/login/kiosk_browsertest.cc
index 0cbe104..ae7c44d 100644
--- a/chrome/browser/chromeos/login/kiosk_browsertest.cc
+++ b/chrome/browser/chromeos/login/kiosk_browsertest.cc
@@ -1399,6 +1399,7 @@
     content::TestNavigationObserver settings_navigation_observer(web_contents,
                                                                  1);
     NavigateParams params(profile, page2, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+    params.disposition = WindowOpenDisposition::NEW_POPUP;
     Navigate(&params);
     // Wait for browser to be handled.
     base::RunLoop waiter;
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc
index aecd62a5..2d7037f 100644
--- a/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/chromeos/login/login_wizard.h"
 #include "chrome/browser/chromeos/login/screens/base_screen.h"
 #include "chrome/browser/chromeos/login/screens/hid_detection_screen.h"
+#include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/login/test/oobe_base_test.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
@@ -97,6 +98,15 @@
     base::RunLoop().RunUntilIdle();
   }
 
+  void ContinueToWelcomeScreen() {
+    // Simulate the user's click on "Continue" button.
+    test::OobeJS()
+        .CreateVisibilityWaiter(true, {"hid-detection", "hid-continue-button"})
+        ->Wait();
+    test::OobeJS().TapOnPath({"hid-detection", "hid-continue-button"});
+    OobeScreenWaiter(WelcomeView::kScreenId).Wait();
+  }
+
  private:
   HIDDetectionScreen* hid_detection_screen_;
   std::unique_ptr<device::FakeInputServiceLinux> fake_input_service_manager_;
@@ -188,9 +198,7 @@
   AddDeviceToService(DeviceType::kKeyboard,
                      device::mojom::InputDeviceType::TYPE_BLUETOOTH);
 
-  // Simulate the user's click on "Continue" button.
-  hid_detection_screen()->OnContinueButtonClicked();
-  OobeScreenWaiter(WelcomeView::kScreenId).Wait();
+  ContinueToWelcomeScreen();
 
   // The adapter should not be powered off at this moment.
   EXPECT_TRUE(adapter()->IsPowered());
@@ -207,12 +215,34 @@
   AddDeviceToService(DeviceType::kKeyboard,
                      device::mojom::InputDeviceType::TYPE_USB);
 
-  // Simulate the user's click on "Continue" button.
-  hid_detection_screen()->OnContinueButtonClicked();
-  OobeScreenWaiter(WelcomeView::kScreenId).Wait();
+  ContinueToWelcomeScreen();
 
   // The adapter should be powered off at this moment.
   EXPECT_FALSE(adapter()->IsPowered());
 }
 
+// Tests that the connected 'ticks' are shown when the devices are connected.
+IN_PROC_BROWSER_TEST_F(HIDDetectionScreenTest, TestTicks) {
+  OobeScreenWaiter(HIDDetectionView::kScreenId).Wait();
+  test::OobeJS()
+      .CreateVisibilityWaiter(false, {"hid-detection", "mouse-tick"})
+      ->Wait();
+  test::OobeJS()
+      .CreateVisibilityWaiter(false, {"hid-detection", "keyboard-tick"})
+      ->Wait();
+
+  AddDeviceToService(DeviceType::kMouse,
+                     device::mojom::InputDeviceType::TYPE_USB);
+  AddDeviceToService(DeviceType::kKeyboard,
+                     device::mojom::InputDeviceType::TYPE_USB);
+
+  test::OobeJS()
+      .CreateVisibilityWaiter(true, {"hid-detection", "mouse-tick"})
+      ->Wait();
+  test::OobeJS()
+      .CreateVisibilityWaiter(true, {"hid-detection", "keyboard-tick"})
+      ->Wait();
+  ContinueToWelcomeScreen();
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
index 99c9d73..a966a40 100644
--- a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
@@ -1532,15 +1532,6 @@
                   nullptr);
   }
 
-  if (policy.has_minimum_chrome_version_enforced()) {
-    const em::StringPolicyProto& container(
-        policy.minimum_chrome_version_enforced());
-    if (container.has_value()) {
-      SetJsonDevicePolicy(key::kMinimumChromeVersionEnforced, container.value(),
-                          policies);
-    }
-  }
-
   if (policy.has_unaffiliated_arc_allowed()) {
     const em::UnaffiliatedArcAllowedProto& container(
         policy.unaffiliated_arc_allowed());
diff --git a/chrome/browser/chromeos/policy/minimum_version_policy_handler_browsertest.cc b/chrome/browser/chromeos/policy/minimum_version_policy_handler_browsertest.cc
index ad575914..6ae0f9e 100644
--- a/chrome/browser/chromeos/policy/minimum_version_policy_handler_browsertest.cc
+++ b/chrome/browser/chromeos/policy/minimum_version_policy_handler_browsertest.cc
@@ -161,7 +161,10 @@
   profile->GetProfilePolicyConnector()->OverrideIsManagedForTesting(true);
 }
 
-IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, CriticalUpdateOnLoginScreen) {
+// TODO(https://crbug.com/1076072): Temporarily disable the test till branch
+// date to avoid unexpected policy behaviour before it's ready to use.
+IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest,
+                       DISABLED_CriticalUpdateOnLoginScreen) {
   EXPECT_EQ(ash::LoginScreenTestApi::GetUsersCount(), 1);
   EXPECT_FALSE(ash::LoginScreenTestApi::IsOobeDialogVisible());
 
@@ -185,7 +188,10 @@
   EXPECT_FALSE(ash::LoginScreenTestApi::IsOobeDialogVisible());
 }
 
-IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, PRE_CriticalUpdateInSession) {
+// TODO(https://crbug.com/1076072): Temporarily disable the test till branch
+// date to avoid unexpected policy behaviour before it's ready to use.
+IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest,
+                       DISABLED_PRE_CriticalUpdateInSession) {
   // Login the user into the session and mark as managed.
   Login();
   MarkUserManaged();
@@ -207,7 +213,10 @@
   EXPECT_TRUE(chrome::IsAttemptingShutdown());
 }
 
-IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, CriticalUpdateInSession) {
+// TODO(https://crbug.com/1076072): Temporarily disable the test till branch
+// date to avoid unexpected policy behaviour before it's ready to use.
+IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest,
+                       DISABLED_CriticalUpdateInSession) {
   // Check login screen is shown post chrome restart due to critical update
   // required in session.
   EXPECT_EQ(session_manager::SessionManager::Get()->session_state(),
@@ -219,8 +228,10 @@
   EXPECT_EQ(user_manager::UserManager::Get()->GetLoggedInUsers().size(), 0u);
 }
 
+// TODO(https://crbug.com/1076072): Temporarily disable the test till branch
+// date to avoid unexpected policy behaviour before it's ready to use.
 IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest,
-                       CriticalUpdateInSessionUnmanagedUser) {
+                       DISABLED_CriticalUpdateInSessionUnmanagedUser) {
   // Login the user into the session.
   Login();
 
@@ -244,8 +255,10 @@
   chromeos::LoginManagerMixin login_manager_{&mixin_host_};
 };
 
+// TODO(https://crbug.com/1076072): Temporarily disable the test till branch
+// date to avoid unexpected policy behaviour before it's ready to use.
 IN_PROC_BROWSER_TEST_F(MinimumVersionNoUsersLoginTest,
-                       CriticalUpdateOnLoginScreen) {
+                       DISABLED_CriticalUpdateOnLoginScreen) {
   chromeos::OobeScreenWaiter(chromeos::GaiaView::kScreenId).Wait();
   EXPECT_EQ(ash::LoginScreenTestApi::GetUsersCount(), 0);
 
@@ -289,8 +302,10 @@
   }
 };
 
+// TODO(https://crbug.com/1076072): Temporarily disable the test till branch
+// date to avoid unexpected policy behaviour before it's ready to use.
 IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyPresentTest,
-                       DeadlineReachedNoUsers) {
+                       DISABLED_DeadlineReachedNoUsers) {
   // Checks update required screen is shown at startup if there is no user in
   // the device.
   EXPECT_EQ(session_manager::SessionManager::Get()->session_state(),
@@ -310,7 +325,10 @@
   chromeos::LoginManagerMixin login_mixin_{&mixin_host_};
 };
 
-IN_PROC_BROWSER_TEST_F(MinimumVersionExistingUserTest, DeadlineReached) {
+// TODO(https://crbug.com/1076072): Temporarily disable the test till branch
+// date to avoid unexpected policy behaviour before it's ready to use.
+IN_PROC_BROWSER_TEST_F(MinimumVersionExistingUserTest,
+                       DISABLED_DeadlineReached) {
   // Checks update required screen is shown at startup if user is existing in
   // the device.
   EXPECT_EQ(session_manager::SessionManager::Get()->session_state(),
@@ -335,7 +353,10 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_F(MinimumVersionBeforeLoginHost, DeadlineReached) {
+// TODO(https://crbug.com/1076072): Temporarily disable the test till branch
+// date to avoid unexpected policy behaviour before it's ready to use.
+IN_PROC_BROWSER_TEST_F(MinimumVersionBeforeLoginHost,
+                       DISABLED_DeadlineReached) {
   // Checks update required screen is shown at startup if the policy handler is
   // invoked before login display host is created.
   EXPECT_EQ(chromeos::LoginDisplayHost::default_host(), nullptr);
@@ -372,8 +393,10 @@
   }
 };
 
+// TODO(https://crbug.com/1076072): Temporarily disable the test till branch
+// date to avoid unexpected policy behaviour before it's ready to use.
 IN_PROC_BROWSER_TEST_F(MinimumVersionPublicSessionAutoLoginTest,
-                       BlockAutoLogin) {
+                       DISABLED_BlockAutoLogin) {
   // Checks public session auto login is blocked if update is required on
   // reboot.
   EXPECT_EQ(session_manager::SessionManager::Get()->session_state(),
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc
index 39abb90..8c814c5 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider.cc
@@ -762,16 +762,6 @@
                                        policy.tpm_firmware_update_settings())));
   }
 
-  if (policy.has_minimum_chrome_version_enforced()) {
-    const em::StringPolicyProto& container(
-        policy.minimum_chrome_version_enforced());
-    if (container.has_value()) {
-      SetJsonDeviceSetting(kMinimumChromeVersionEnforced,
-                           policy::key::kMinimumChromeVersionEnforced,
-                           container.value(), new_values_cache);
-    }
-  }
-
   if (policy.has_cast_receiver_name()) {
     const em::CastReceiverNameProto& container(policy.cast_receiver_name());
     if (container.has_name()) {
diff --git a/chrome/browser/component_updater/registration.cc b/chrome/browser/component_updater/registration.cc
index 3b985d0..66046f8f 100644
--- a/chrome/browser/component_updater/registration.cc
+++ b/chrome/browser/component_updater/registration.cc
@@ -47,6 +47,7 @@
 #if !defined(OS_ANDROID)
 #include "chrome/browser/component_updater/intervention_policy_database_component_installer.h"
 #include "chrome/browser/component_updater/soda_component_installer.h"
+#include "chrome/browser/enterprise/connectors/service_providers.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
 #endif
 
@@ -192,6 +193,10 @@
 #if defined(OS_CHROMEOS)
   RegisterSmartDimComponent(cus);
 #endif  // !defined(OS_CHROMEOS)
+
+#if !defined(OS_ANDROID)
+  enterprise_connectors::RegisterServiceProvidersComponent(cus);
+#endif
 }
 
 }  // namespace component_updater
diff --git a/chrome/browser/enterprise/connectors/OWNERS b/chrome/browser/enterprise/connectors/OWNERS
index 3b28b37d..10c7df8 100644
--- a/chrome/browser/enterprise/connectors/OWNERS
+++ b/chrome/browser/enterprise/connectors/OWNERS
@@ -1,2 +1,5 @@
 rogerta@chromium.org
 domfc@chromium.org
+
+per-file service_providers.*=file://components/component_updater/OWNERS
+
diff --git a/chrome/browser/enterprise/connectors/service_providers.cc b/chrome/browser/enterprise/connectors/service_providers.cc
new file mode 100644
index 0000000..76c71b4
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/service_providers.cc
@@ -0,0 +1,163 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/enterprise/connectors/service_providers.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/values.h"
+#include "components/component_updater/component_installer.h"
+#include "components/component_updater/component_updater_service.h"
+#include "components/update_client/update_client.h"
+
+namespace {
+
+const base::FilePath::CharType kConfigFileName[] =
+    FILE_PATH_LITERAL("service_providers.json");
+
+// The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
+// The extension id is: dfcoifdifjfolmglbbogapfcihdgckga
+// SHA256: 352e8538595ebc6b11e60f5287362a60a4c4f0c2fb3595c43621942ffbd973b5
+const uint8_t kPublicKeySHA256[32] = {
+    0x35, 0x2e, 0x85, 0x38, 0x59, 0x5e, 0xbc, 0x6b, 0x11, 0xe6, 0x0f,
+    0x52, 0x87, 0x36, 0x2a, 0x60, 0xa4, 0xc4, 0xf0, 0xc2, 0xfb, 0x35,
+    0x95, 0xc4, 0x36, 0x21, 0x94, 0x2f, 0xfb, 0xd9, 0x73, 0xb5};
+
+base::Optional<base::Value> LoadConfigFromDisk(const base::FilePath& path) {
+  // TODO(crbug/1081375): replace this with a safe read function.
+  std::string json;
+  if (!base::ReadFileToString(path, &json)) {
+    return base::nullopt;
+  }
+
+  return base::JSONReader::Read(json);
+}
+
+}  // namespace
+
+namespace enterprise_connectors {
+
+class ServiceProvidersConfigPolicy
+    : public component_updater::ComponentInstallerPolicy {
+ public:
+  ServiceProvidersConfigPolicy();
+  ~ServiceProvidersConfigPolicy() override;
+
+  const base::Value& GetServiceProvidersConfig() const { return config_; }
+
+ private:
+  void SetConfig(base::Optional<base::Value> config);
+
+  // component_updater::ComponentInstallerPolicy overrides:
+  bool VerifyInstallation(const base::DictionaryValue& manifest,
+                          const base::FilePath& install_dir) const override;
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override;
+  bool RequiresNetworkEncryption() const override;
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::DictionaryValue& manifest,
+      const base::FilePath& install_dir) override;
+  void OnCustomUninstall() override;
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      std::unique_ptr<base::DictionaryValue> manifest) override;
+  base::FilePath GetRelativeInstallDir() const override;
+  void GetHash(std::vector<uint8_t>* hash) const override;
+  std::string GetName() const override;
+  std::vector<std::string> GetMimeTypes() const override;
+  update_client::InstallerAttributes GetInstallerAttributes() const override;
+
+  base::Value config_;
+  base::WeakPtrFactory<ServiceProvidersConfigPolicy> factory_{this};
+};
+
+ServiceProvidersConfigPolicy::ServiceProvidersConfigPolicy() = default;
+ServiceProvidersConfigPolicy::~ServiceProvidersConfigPolicy() = default;
+
+void ServiceProvidersConfigPolicy::SetConfig(
+    base::Optional<base::Value> config) {
+  if (!config.has_value())
+    return;
+
+  config_ = std::move(config.value());
+}
+
+bool ServiceProvidersConfigPolicy::VerifyInstallation(
+    const base::DictionaryValue& manifest,
+    const base::FilePath& install_dir) const {
+  const base::FilePath config_file = install_dir.Append(kConfigFileName);
+  return base::PathExists(config_file);
+}
+
+bool ServiceProvidersConfigPolicy::SupportsGroupPolicyEnabledComponentUpdates()
+    const {
+  return false;
+}
+
+bool ServiceProvidersConfigPolicy::RequiresNetworkEncryption() const {
+  return false;
+}
+
+update_client::CrxInstaller::Result
+ServiceProvidersConfigPolicy::OnCustomInstall(
+    const base::DictionaryValue& manifest,
+    const base::FilePath& install_dir) {
+  return update_client::CrxInstaller::Result(0);  // Nothing custom here.
+}
+
+void ServiceProvidersConfigPolicy::OnCustomUninstall() {}
+
+void ServiceProvidersConfigPolicy::ComponentReady(
+    const base::Version& version,
+    const base::FilePath& install_dir,
+    std::unique_ptr<base::DictionaryValue> manifest) {
+  const base::FilePath config_file = install_dir.Append(kConfigFileName);
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(&LoadConfigFromDisk, config_file),
+      base::BindOnce(&ServiceProvidersConfigPolicy::SetConfig,
+                     factory_.GetWeakPtr()));
+}
+
+base::FilePath ServiceProvidersConfigPolicy::GetRelativeInstallDir() const {
+  return base::FilePath(FILE_PATH_LITERAL("ECSerivceProvidersConfig"));
+}
+
+void ServiceProvidersConfigPolicy::GetHash(std::vector<uint8_t>* hash) const {
+  hash->assign(kPublicKeySHA256,
+               kPublicKeySHA256 + base::size(kPublicKeySHA256));
+}
+
+std::string ServiceProvidersConfigPolicy::GetName() const {
+  return "Enterprise Connectors Service Providers Configuration";
+}
+
+std::vector<std::string> ServiceProvidersConfigPolicy::GetMimeTypes() const {
+  return std::vector<std::string>();
+}
+
+update_client::InstallerAttributes
+ServiceProvidersConfigPolicy::GetInstallerAttributes() const {
+  return update_client::InstallerAttributes();
+}
+
+void RegisterServiceProvidersComponent(
+    component_updater::ComponentUpdateService* cus) {
+  auto installer = base::MakeRefCounted<component_updater::ComponentInstaller>(
+      std::make_unique<ServiceProvidersConfigPolicy>());
+  installer->Register(cus, base::OnceClosure());
+}
+
+}  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/service_providers.h b/chrome/browser/enterprise/connectors/service_providers.h
new file mode 100644
index 0000000..3b11ab7
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/service_providers.h
@@ -0,0 +1,21 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ENTERPRISE_CONNECTORS_SERVICE_PROVIDERS_H_
+#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_SERVICE_PROVIDERS_H_
+
+namespace component_updater {
+class ComponentUpdateService;
+}
+
+namespace enterprise_connectors {
+
+// Registers a component extension with the update service which downloads
+// and verifies service provider configurations used by enterprise connectors.
+void RegisterServiceProvidersComponent(
+    component_updater::ComponentUpdateService* cus);
+
+}  // namespace enterprise_connectors
+
+#endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_SERVICE_PROVIDERS_H_
diff --git a/chrome/browser/file_util_service.cc b/chrome/browser/file_util_service.cc
index e3a4d884..b4369b8 100644
--- a/chrome/browser/file_util_service.cc
+++ b/chrome/browser/file_util_service.cc
@@ -13,7 +13,6 @@
   content::ServiceProcessHost::Launch<chrome::mojom::FileUtilService>(
       remote.InitWithNewPipeAndPassReceiver(),
       content::ServiceProcessHost::Options()
-          .WithSandboxType(service_manager::SandboxType::kUtility)
           .WithDisplayName(IDS_UTILITY_PROCESS_FILE_UTILITY_NAME)
           .Pass());
   return remote;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index af5ad49..1870aa2 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -641,6 +641,11 @@
     "expiry_milestone": 85
   },
   {
+    "name": "cross-origin-opener-policy-reporting",
+    "owners": [ "ahemery", "clamy", "pmeuleman" ],
+    "expiry_milestone": 85
+  },
+  {
     "name": "crostini-disk-resizing",
     "owners": [ "davidmunro", "nverne" ],
     "expiry_milestone": 86
@@ -3384,6 +3389,11 @@
     "expiry_milestone": 88
   },
   {
+    "name": "page-info-version-2",
+    "owners": [ "eokoyomon", "dullweber" ],
+    "expiry_milestone": 88
+  },
+  {
     "name": "paint-preview-demo",
     "owners": [ "ckitagawa", "fredmello", "mahmoudi" ],
     "expiry_milestone": 88
@@ -3545,6 +3555,11 @@
     "expiry_milestone": 88
   },
   {
+    "name": "query-tiles-enable-query-editing",
+    "owners": [ "shaktisahu"],
+    "expiry_milestone": 88
+  },
+  {
     "name": "query-tiles-instant-fetch",
     "owners": [ "xingliu"],
     "expiry_milestone": 88
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 5c73b18..2ca838b 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -762,6 +762,11 @@
 const char kCrossOriginOpenerPolicyDescription[] =
     "Enables Cross Origin Opener Policy.";
 
+const char kCrossOriginOpenerPolicyReportingName[] =
+    "Cross Origin Opener Policy reporting";
+const char kCrossOriginOpenerPolicyReportingDescription[] =
+    "Enables Cross Origin Opener Policy reporting.";
+
 const char kDisableKeepaliveFetchName[] = "Disable fetch with keepalive set";
 const char kDisableKeepaliveFetchDescription[] =
     "Disable fetch with keepalive set "
@@ -2597,6 +2602,10 @@
 const char kPageInfoPerformanceHintsDescription[] =
     "Show site performance information in the page info menu.";
 
+const char kPageInfoV2Name[] = "Page info version two";
+const char kPageInfoV2Description[] =
+    "Enable the second version of the page info menu.";
+
 const char kPasswordManagerOnboardingAndroidName[] =
     "Password manager onboarding experience";
 const char kPasswordManagerOnboardingAndroidDescription[] =
@@ -2632,6 +2641,11 @@
 const char kQueryTilesDescription[] = "Shows query tiles in Chrome";
 const char kQueryTilesOmniboxName[] = "Show query tiles in omnibox";
 const char kQueryTilesOmniboxDescription[] = "Shows query tiles in omnibox";
+const char kQueryTilesEnableQueryEditingName[] =
+    "Query Tiles - Enable query edit mode";
+const char kQueryTilesEnableQueryEditingDescription[] =
+    "When a query tile is tapped, the query text will be shown in the omnibox "
+    "and user will have a chance to edit the text before submitting";
 const char kQueryTilesCountryCode[] = "Country code for getting tiles";
 const char kQueryTilesCountryCodeDescription[] =
     "When query tiles are enabled, this value determines tiles for which "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index f153e53..683ca3a 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -437,6 +437,9 @@
 extern const char kCrossOriginOpenerPolicyName[];
 extern const char kCrossOriginOpenerPolicyDescription[];
 
+extern const char kCrossOriginOpenerPolicyReportingName[];
+extern const char kCrossOriginOpenerPolicyReportingDescription[];
+
 extern const char kDisableKeepaliveFetchName[];
 extern const char kDisableKeepaliveFetchDescription[];
 
@@ -1512,6 +1515,9 @@
 extern const char kPageInfoPerformanceHintsName[];
 extern const char kPageInfoPerformanceHintsDescription[];
 
+extern const char kPageInfoV2Name[];
+extern const char kPageInfoV2Description[];
+
 extern const char kPasswordManagerOnboardingAndroidName[];
 extern const char kPasswordManagerOnboardingAndroidDescription[];
 
@@ -1528,6 +1534,8 @@
 extern const char kQueryTilesDescription[];
 extern const char kQueryTilesOmniboxName[];
 extern const char kQueryTilesOmniboxDescription[];
+extern const char kQueryTilesEnableQueryEditingName[];
+extern const char kQueryTilesEnableQueryEditingDescription[];
 extern const char kQueryTilesCountryCode[];
 extern const char kQueryTilesCountryCodeDescription[];
 extern const char kQueryTilesCountryCodeUS[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 6bc30df..3b90058 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -257,6 +257,7 @@
     &subresource_filter::kSafeBrowsingSubresourceFilter,
     &upboarding::features::kQueryTiles,
     &upboarding::features::kQueryTilesInOmnibox,
+    &upboarding::features::kQueryTilesEnableQueryEditing,
 };
 
 const base::Feature* FindFeatureExposedToJava(const std::string& feature_name) {
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 4886c47f..b336df6ed 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -353,6 +353,7 @@
     public static final String PRIORITIZE_BOOTSTRAP_TASKS = "PrioritizeBootstrapTasks";
     public static final String PROBABILISTIC_CRYPTID_RENDERER = "ProbabilisticCryptidRenderer";
     public static final String QUERY_TILES = "QueryTiles";
+    public static final String QUERY_TILES_ENABLE_QUERY_EDITING = "QueryTilesEnableQueryEditing";
     public static final String QUERY_IN_OMNIBOX = "QueryInOmnibox";
     public static final String QUIET_NOTIFICATION_PROMPTS = "QuietNotificationPrompts";
     public static final String REACHED_CODE_PROFILER = "ReachedCodeProfiler";
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
index 61e190a0..a8dffd6 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
@@ -404,9 +404,10 @@
 
   TestMetricsRecordedAndMaybeInterstitialShown(
       browser(), kNavigatedUrl, kExpectedSuggestedUrl,
-      NavigationSuggestionEvent::kMatchTopSite);
+      NavigationSuggestionEvent::kMatchSkeletonTop500);
 
-  CheckUkm({kNavigatedUrl}, "MatchType", LookalikeUrlMatchType::kTopSite);
+  CheckUkm({kNavigatedUrl}, "MatchType",
+           LookalikeUrlMatchType::kSkeletonMatchTop500);
 }
 
 // Embedding a top domain should show an interstitial when enabled. If disabled
@@ -463,8 +464,10 @@
   TestInterstitialNotShown(browser(), kNavigatedUrl);
   histograms.ExpectTotalCount(lookalikes::kHistogramName, 1);
   histograms.ExpectBucketCount(lookalikes::kHistogramName,
-                               NavigationSuggestionEvent::kMatchTopSite, 1);
-  CheckUkm({kNavigatedUrl}, "MatchType", LookalikeUrlMatchType::kTopSite);
+                               NavigationSuggestionEvent::kMatchSkeletonTop5k,
+                               1);
+  CheckUkm({kNavigatedUrl}, "MatchType",
+           LookalikeUrlMatchType::kSkeletonMatchTop5k);
 }
 
 // Same as Idn_TopDomain_Match, but this time the domain contains characters
@@ -481,9 +484,10 @@
 
   TestMetricsRecordedAndMaybeInterstitialShown(
       browser(), kNavigatedUrl, kExpectedSuggestedUrl,
-      NavigationSuggestionEvent::kMatchTopSite);
+      NavigationSuggestionEvent::kMatchSkeletonTop500);
 
-  CheckUkm({kNavigatedUrl}, "MatchType", LookalikeUrlMatchType::kTopSite);
+  CheckUkm({kNavigatedUrl}, "MatchType",
+           LookalikeUrlMatchType::kSkeletonMatchTop500);
 }
 
 // The navigated domain will fall back to punycode because it fails spoof checks
diff --git a/chrome/browser/media/cast_mirroring_service_host.cc b/chrome/browser/media/cast_mirroring_service_host.cc
index 2ad4828..efa09c31 100644
--- a/chrome/browser/media/cast_mirroring_service_host.cc
+++ b/chrome/browser/media/cast_mirroring_service_host.cc
@@ -210,7 +210,6 @@
       mirroring_service_.BindNewPipeAndPassReceiver(),
       content::ServiceProcessHost::Options()
           .WithDisplayName("Mirroring Service")
-          .WithSandboxType(service_manager::SandboxType::kUtility)
           .Pass());
   mojo::PendingRemote<mojom::ResourceProvider> provider;
   resource_provider_receiver.Bind(provider.InitWithNewPipeAndPassReceiver());
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 2a6a62d..657410c5 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -3206,57 +3206,6 @@
   EXPECT_EQ(JSIncrementerFetch(contents), 3);
 }
 
-#if defined(OS_WIN)
-
-class ForceNetworkInProcessTest
-    : public InProcessBrowserTest,
-      public ::testing::WithParamInterface<
-          /*policy::key::kForceNetworkInProcess=*/bool> {
- public:
-  // InProcessBrowserTest implementation:
-  void SetUp() override {
-    EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
-        .WillRepeatedly(testing::Return(true));
-    policy::PolicyMap values;
-    values.Set(policy::key::kForceNetworkInProcess,
-               policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
-               policy::POLICY_SOURCE_CLOUD,
-               std::make_unique<base::Value>(GetParam()), nullptr);
-    policy_provider_.UpdateChromePolicy(values);
-    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
-        &policy_provider_);
-
-    InProcessBrowserTest::SetUp();
-  }
-
- private:
-  policy::MockConfigurationPolicyProvider policy_provider_;
-};
-
-IN_PROC_BROWSER_TEST_P(ForceNetworkInProcessTest, IsRespected) {
-  bool expected_in_process = GetParam();
-
-  // When run with --enable-features=NetworkServiceInProcess, the Network
-  // Service will always be in process. This configuration is used on some
-  // bots - see https://crbug.com/1002752.
-  expected_in_process |=
-      base::FeatureList::IsEnabled(features::kNetworkServiceInProcess);
-
-  ASSERT_EQ(expected_in_process, content::IsInProcessNetworkService());
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    Enabled,
-    ForceNetworkInProcessTest,
-    ::testing::Values(/*policy::key::kForceNetworkInProcess=*/true));
-
-INSTANTIATE_TEST_SUITE_P(
-    Disabled,
-    ForceNetworkInProcessTest,
-    ::testing::Values(/*policy::key::kForceNetworkInProcess=*/false));
-
-#endif  // defined(OS_WIN)
-
 #if !defined(OS_ANDROID)
 
 // The possibilities for a boolean policy.
diff --git a/chrome/browser/reputation/local_heuristics.cc b/chrome/browser/reputation/local_heuristics.cc
index c489e03c..9ab2e23d 100644
--- a/chrome/browser/reputation/local_heuristics.cc
+++ b/chrome/browser/reputation/local_heuristics.cc
@@ -64,8 +64,6 @@
   *safe_url = GURL(std::string(url::kHttpScheme) +
                    url::kStandardSchemeSeparator + matched_domain);
   switch (match_type) {
-    case LookalikeUrlMatchType::kTopSite:
-      return kEnableLookalikeTopSites.Get();
     case LookalikeUrlMatchType::kEditDistance:
       return kEnableLookalikeEditDistance.Get();
     case LookalikeUrlMatchType::kEditDistanceSiteEngagement:
@@ -73,12 +71,15 @@
     case LookalikeUrlMatchType::kTargetEmbedding:
       return kEnableLookalikeTargetEmbedding.Get();
     case LookalikeUrlMatchType::kSiteEngagement:
-      // We should only ever reach this case when the
-      // kLookalikeUrlNavigationSuggestionsUI feature is disabled. Otherwise, an
-      // interstitial will already be shown on the kSiteEngagement match type.
+    case LookalikeUrlMatchType::kSkeletonMatchTop500:
+      // We should only ever reach these cases when the lookalike interstitial
+      // is disabled. Now that interstitial is fully launched, this only happens
+      // in tests.
       DCHECK(!base::FeatureList::IsEnabled(
           features::kLookalikeUrlNavigationSuggestionsUI));
       return true;
+    case LookalikeUrlMatchType::kSkeletonMatchTop5k:
+      return kEnableLookalikeTopSites.Get();
     case LookalikeUrlMatchType::kNone:
       NOTREACHED();
   }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
index 485516e40..f941c38c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -2546,3 +2546,24 @@
     node.focus();
   });
 });
+
+TEST_F('ChromeVoxBackgroundTest', 'MenuItemRadio', function() {
+  const mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(
+      `
+    <ul role="menu" tabindex="0" autofocus>
+      <li role="menuitemradio" aria-checked="true">Small</li>
+      <li role="menuitemradio" aria-checked="false">Medium</li>
+      <li role="menuitemradio" aria-checked="false">Large</li>
+    </ul>
+  `,
+      function(root) {
+        mockFeedback.expectSpeech('Menu', 'with 3 items')
+            .call(doCmd('nextObject'))
+            .expectSpeech('Small, menu item radio button selected', ' 1 of 3 ')
+            .call(doCmd('nextObject'))
+            .expectSpeech(
+                'Medium, menu item radio button unselected', ' 2 of 3 ')
+            .replay();
+      });
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js
index b2e5c776..7efaafad 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js
@@ -2511,8 +2511,8 @@
     },
     menuItemRadio: {
       speak: `$if($checked, $earcon(CHECK_ON), $earcon(CHECK_OFF))
-          $if($checked, @describe_radio_selected($name),
-          @describe_radio_unselected($name)) $state $roleDescription
+          $if($checked, @describe_menu_item_radio_selected($name),
+          @describe_menu_item_radio_unselected($name)) $state $roleDescription
           $restriction $description
           @describe_index($posInSet, $setSize)`
     },
diff --git a/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp b/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp
index b897a6b..75c81625 100644
--- a/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp
+++ b/chrome/browser/resources/chromeos/accessibility/strings/chromevox_strings.grdp
@@ -430,6 +430,12 @@
   <message desc="Describes a HTML radio button named 'name' in the unselected state." name="IDS_CHROMEVOX_DESCRIBE_RADIO_UNSELECTED">
     <ph name="name">$1</ph>, radio button unselected
   </message>
+  <message desc="Describes a HTML menu item radio button named 'name' in the selected state." name="IDS_CHROMEVOX_DESCRIBE_MENU_ITEM_RADIO_SELECTED">
+    <ph name="name">$1</ph>, menu item radio button selected
+  </message>
+  <message desc="Describes a HTML menu item radio button named 'name' in the unselected state." name="IDS_CHROMEVOX_DESCRIBE_MENU_ITEM_RADIO_UNSELECTED">
+    <ph name="name">$1</ph>, menu item radio button unselected
+  </message>
   <message desc="Describes a window named 'name'." name="IDS_CHROMEVOX_DESCRIBE_WINDOW">
     <ph name="name">$1</ph>, window
   </message>
diff --git a/chrome/browser/resources/chromeos/login/oobe_hid_detection.html b/chrome/browser/resources/chromeos/login/oobe_hid_detection.html
index d99f9a1..c522279 100644
--- a/chrome/browser/resources/chromeos/login/oobe_hid_detection.html
+++ b/chrome/browser/resources/chromeos/login/oobe_hid_detection.html
@@ -46,7 +46,7 @@
   </svg>
 </iron-iconset-svg>
 
-<dom-module id="oobe-hid-detection">
+<dom-module id="hid-detection">
   <template>
     <style include="oobe-dialog-host"></style>
     <link rel="stylesheet" href="oobe_hid_detection.css">
@@ -89,6 +89,7 @@
       <div slot="bottom-buttons" class="flex layout horizontal end-justified">
         <oobe-text-button inverse on-tap="onHIDContinueTap_"
             text-key="hidDetectionContinue"
+            id="hid-continue-button"
             disabled="[[!continueButtonEnabled]]"></oobe-text-button>
       </div>
     </oobe-dialog>
diff --git a/chrome/browser/resources/chromeos/login/oobe_hid_detection.js b/chrome/browser/resources/chromeos/login/oobe_hid_detection.js
index 242d99d..7992f5f 100644
--- a/chrome/browser/resources/chromeos/login/oobe_hid_detection.js
+++ b/chrome/browser/resources/chromeos/login/oobe_hid_detection.js
@@ -20,7 +20,7 @@
 };
 
 Polymer({
-  is: 'oobe-hid-detection',
+  is: 'hid-detection',
 
   behaviors: [OobeI18nBehavior, OobeDialogHostBehavior, LoginScreenBehavior],
 
diff --git a/chrome/browser/resources/chromeos/login/oobe_screens.html b/chrome/browser/resources/chromeos/login/oobe_screens.html
index 10ce9e34..615287a 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screens.html
+++ b/chrome/browser/resources/chromeos/login/oobe_screens.html
@@ -12,9 +12,9 @@
 </kiosk-enable>
 <include src="oobe_screen_update.html">
 <include src="oobe_screen_auto_enrollment_check.html">
-<oobe-hid-detection id="hid-detection" class="step right hidden"
+<hid-detection id="hid-detection" class="step right hidden"
     full-screen-dialog>
-</oobe-hid-detection>
+</hid-detection>
 <include src="oobe_screen_supervision_transition.html">
 <include src="oobe_screen_demo_setup.html">
 <include src="oobe_screen_demo_preferences.html">
diff --git a/chrome/browser/resources/settings/safety_check_page/BUILD.gn b/chrome/browser/resources/settings/safety_check_page/BUILD.gn
index 60c090d0..c33108c7 100644
--- a/chrome/browser/resources/settings/safety_check_page/BUILD.gn
+++ b/chrome/browser/resources/settings/safety_check_page/BUILD.gn
@@ -11,7 +11,12 @@
   closure_flags = settings_closure_flags
   deps = [
     ":safety_check_browser_proxy",
+    ":safety_check_child",
+    ":safety_check_extensions_element",
     ":safety_check_page",
+    ":safety_check_passwords_element",
+    ":safety_check_safe_browsing_element",
+    ":safety_check_updates_element",
   ]
 }
 
@@ -19,15 +24,34 @@
   deps = [ "//ui/webui/resources/js:cr.m" ]
 }
 
+js_library("safety_check_child") {
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
+  ]
+}
+
+js_library("safety_check_extensions_element") {
+  deps = [
+    ":safety_check_child",
+    "..:metrics_browser_proxy",
+    "..:open_window_proxy",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
+  ]
+}
+
 js_library("safety_check_page") {
   deps = [
     ":safety_check_browser_proxy",
+    ":safety_check_extensions_element",
+    ":safety_check_passwords_element",
+    ":safety_check_safe_browsing_element",
+    ":safety_check_updates_element",
     "..:hats_browser_proxy",
-    "..:lifetime_browser_proxy.m",
     "..:metrics_browser_proxy",
-    "..:open_window_proxy",
-    "..:route",
-    "..:router.m",
     "../autofill_page:password_manager_proxy",
     "//third_party/polymer/v3_0/components-chromium/iron-a11y-announcer:iron-a11y-announcer",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
@@ -37,6 +61,50 @@
   ]
 }
 
+js_library("safety_check_passwords_element") {
+  deps = [
+    ":safety_check_child",
+    "..:metrics_browser_proxy",
+    "..:route",
+    "..:router.m",
+    "../autofill_page:password_manager_proxy",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
+  ]
+}
+
+js_library("safety_check_safe_browsing_element") {
+  deps = [
+    ":safety_check_child",
+    "..:metrics_browser_proxy",
+    "..:route",
+    "..:router.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
+  ]
+}
+
+js_library("safety_check_updates_element") {
+  deps = [
+    ":safety_check_child",
+    "..:lifetime_browser_proxy.m",
+    "..:metrics_browser_proxy",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
+  ]
+}
+
 html_to_js("web_components") {
-  js_files = [ "safety_check_page.js" ]
+  js_files = [
+    "safety_check_child.js",
+    "safety_check_extensions_element.js",
+    "safety_check_page.js",
+    "safety_check_passwords_element.js",
+    "safety_check_safe_browsing_element.js",
+    "safety_check_updates_element.js",
+  ]
 }
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_child.html b/chrome/browser/resources/settings/safety_check_page/safety_check_child.html
new file mode 100644
index 0000000..c28ebf4
--- /dev/null
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_child.html
@@ -0,0 +1,53 @@
+<style include="cr-shared-style settings-shared iron-flex">
+  iron-icon {
+    display: flex;
+    flex-shrink: 0;
+    padding-inline-end: var(--cr-icon-button-margin-start);
+    width: var(--cr-link-row-icon-width, var(--cr-icon-size));
+  }
+
+  .icon-blue {
+    fill: var(--google-blue-600);
+  }
+
+  .icon-red {
+    fill: var(--google-red-600);
+  }
+
+  /* dark mode */
+  @media (prefers-color-scheme: dark) {
+    .icon-blue {
+      fill: var(--google-blue-refresh-300);
+    }
+
+    .icon-red {
+      fill: var(--google-red-refresh-300);
+    }
+  }
+</style>
+<div class="cr-row">
+  <iron-icon id="statusIcon"
+      icon="[[getStatusIcon_(iconStatus)]]"
+      src="[[getStatusIconSrc_(iconStatus)]]"
+      class$="[[getStatusIconClass_(iconStatus)]]"
+      role="img"
+      aria-label="[[getStatusIconAriaLabel_(iconStatus)]]">
+  </iron-icon>
+  <div class="flex cr-padded-text">
+    <div id="label">[[label]]</div>
+    <div id="subLabel" class="secondary" no-search
+        inner-h-t-m-l="[[subLabel]]">
+    </div>
+  </div>
+  <template is="dom-if" if="[[showButton_(buttonLabel)]]" restamp>
+    <div class="separator"></div>
+    <cr-button id="button" class$="[[buttonClass]]" on-click="onButtonClick_"
+        aria-label="[[buttonAriaLabel]]" no-search>
+      [[buttonLabel]]
+    </cr-button>
+  </template>
+  <template is="dom-if" if="[[showManagedIcon_(managedIcon)]]" restamp>
+    <iron-icon id="managedIcon" icon="[[managedIcon]]" aria-hidden="true">
+    </iron-icon>
+  </template>
+</div>
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_child.js b/chrome/browser/resources/settings/safety_check_page/safety_check_child.js
new file mode 100644
index 0000000..c4db1d2af
--- /dev/null
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_child.js
@@ -0,0 +1,160 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'settings-safety-check-element' bundles functionality safety check elements
+ * have in common. It is used by all safety check elements: parent, updates,
+ * passwors, etc.
+ */
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/shared_style_css.m.js';
+import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
+import '../settings_shared_css.m.js';
+
+import {assertNotReached} from 'chrome://resources/js/assert.m.js';
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+/**
+ * UI states a safety check child can be in. Defines the basic UI of the child.
+ * @enum {number}
+ */
+export const SafetyCheckIconStatus = {
+  RUNNING: 0,
+  SAFE: 1,
+  INFO: 2,
+  WARNING: 3,
+};
+
+Polymer({
+  is: 'settings-safety-check-child',
+
+  _template: html`{__html_template__}`,
+
+  behaviors: [I18nBehavior],
+
+  properties: {
+    /**
+     * Status of the left hand icon.
+     * {!SafetyCheckParentStatus}
+     */
+    iconStatus: {
+      type: Number,
+      value: SafetyCheckIconStatus.RUNNING,
+    },
+
+    /**Primary label of the element. */
+    label: String,
+
+    /** Secondary label of the element. */
+    subLabel: String,
+
+    /** Text of the right hand button. |null| removes it from the DOM. */
+    buttonLabel: String,
+
+    /** Aria label of the right hand button. */
+    buttonAriaLabel: String,
+
+    /** Classes of the right hand button. */
+    buttonClass: String,
+
+    /** Right hand managed icon. |null| removes it from the DOM. */
+    managedIcon: String,
+  },
+
+  /**
+   * Returns the left hand icon for an icon status.
+   * @private
+   * @return {?string}
+   */
+  getStatusIcon_: function() {
+    switch (this.iconStatus) {
+      case SafetyCheckIconStatus.RUNNING:
+        return null;
+      case SafetyCheckIconStatus.SAFE:
+        return 'cr:check';
+      case SafetyCheckIconStatus.INFO:
+        return 'cr:info';
+      case SafetyCheckIconStatus.WARNING:
+        return 'cr:warning';
+      default:
+        assertNotReached();
+    }
+  },
+
+  /**
+   * Returns the left hand icon src for an icon status.
+   * @private
+   * @return {?string}
+   */
+  getStatusIconSrc_: function() {
+    if (this.iconStatus === SafetyCheckIconStatus.RUNNING) {
+      return 'chrome://resources/images/throbber_small.svg';
+    }
+    return null;
+  },
+
+  /**
+   * Returns the left hand icon class for an icon status.
+   * @private
+   * @return {string}
+   */
+  getStatusIconClass_: function() {
+    switch (this.iconStatus) {
+      case SafetyCheckIconStatus.RUNNING:
+      case SafetyCheckIconStatus.SAFE:
+        return 'icon-blue';
+      case SafetyCheckIconStatus.WARNING:
+        return 'icon-red';
+      default:
+        return '';
+    }
+  },
+
+  /**
+   * Returns the left hand icon aria label for an icon status.
+   * @private
+   * @return {string}
+   */
+  getStatusIconAriaLabel_: function() {
+    switch (this.iconStatus) {
+      case SafetyCheckIconStatus.RUNNING:
+        return this.i18n('safetyCheckIconRunningAriaLabel');
+      case SafetyCheckIconStatus.SAFE:
+        return this.i18n('safetyCheckIconSafeAriaLabel');
+      case SafetyCheckIconStatus.INFO:
+        return this.i18n('safetyCheckIconInfoAriaLabel');
+      case SafetyCheckIconStatus.WARNING:
+        return this.i18n('safetyCheckIconWarningAriaLabel');
+      default:
+        assertNotReached();
+    }
+  },
+
+  /**
+   * If the right-hand side button should be shown.
+   * @private
+   * @return {boolean}
+   */
+  showButton_: function() {
+    return !!this.buttonLabel;
+  },
+
+  /** @private */
+  onButtonClick_: function() {
+    this.fire('button-click');
+  },
+
+  /**
+   * If the right-hand side managed icon should be shown.
+   * @private
+   * @return {boolean}
+   */
+  showManagedIcon_: function() {
+    return !!this.managedIcon;
+  },
+});
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_extensions_element.html b/chrome/browser/resources/settings/safety_check_page/safety_check_extensions_element.html
new file mode 100644
index 0000000..937aca68b
--- /dev/null
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_extensions_element.html
@@ -0,0 +1,11 @@
+<settings-safety-check-child
+  id="safetyCheckChild"
+  icon-status="[[getIconStatus_(status_)]]"
+  label="$i18n{safetyCheckExtensionsPrimaryLabel}"
+  sub-label="[[displayString_]]"
+  button-label="[[getButtonLabel_(status_)]]"
+  button-aria-label="$i18n{safetyCheckExtensionsButtonAriaLabel}"
+  button-class="[[getButtonClass_(status_)]]"
+  on-button-click="onButtonClick_"
+  managed-icon="[[getManagedIcon_(status_)]]">
+</settings-safety-check-child>
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_extensions_element.js b/chrome/browser/resources/settings/safety_check_page/safety_check_extensions_element.js
new file mode 100644
index 0000000..7d18036
--- /dev/null
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_extensions_element.js
@@ -0,0 +1,149 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'settings-safety-extensions-element' is the settings page containing the
+ * safety check element showing the extension status.
+ */
+import {assertNotReached} from 'chrome://resources/js/assert.m.js';
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {MetricsBrowserProxy, MetricsBrowserProxyImpl, SafetyCheckInteractions} from '../metrics_browser_proxy.js';
+import {OpenWindowProxyImpl} from '../open_window_proxy.js';
+
+import {SafetyCheckCallbackConstants, SafetyCheckExtensionsStatus} from './safety_check_browser_proxy.js';
+import {SafetyCheckIconStatus} from './safety_check_child.js';
+
+/**
+ * @typedef {{
+ *   newState: SafetyCheckExtensionsStatus,
+ *   displayString: string,
+ * }}
+ */
+let ExtensionssChangedEvent;
+
+Polymer({
+  is: 'settings-safety-check-extensions-element',
+
+  _template: html`{__html_template__}`,
+
+  behaviors: [
+    I18nBehavior,
+    WebUIListenerBehavior,
+  ],
+
+  properties: {
+    /**
+     * Current state of the safety check extensions element.
+     * @private {!SafetyCheckExtensionsStatus}
+     */
+    status_: {
+      type: Number,
+      value: SafetyCheckExtensionsStatus.CHECKING,
+    },
+
+    /** UI string to display for this child, received from the backend. */
+    displayString_: String,
+  },
+
+  /** ?MetricsBrowserProxy */
+  metricsBrowserProxy_: null,
+
+  /** @override */
+  attached: function() {
+    this.metricsBrowserProxy_ = MetricsBrowserProxyImpl.getInstance();
+
+    // Register for safety check status updates.
+    this.addWebUIListener(
+        SafetyCheckCallbackConstants.EXTENSIONS_CHANGED,
+        this.onSafetyCheckExtensionsChanged_.bind(this));
+  },
+
+  /**
+   * @param {!ExtensionssChangedEvent} event
+   * @private
+   */
+  onSafetyCheckExtensionsChanged_: function(event) {
+    this.status_ = event.newState;
+    this.displayString_ = event.displayString;
+  },
+
+  /**
+   * @return {SafetyCheckIconStatus}
+   * @private
+   */
+  getIconStatus_: function() {
+    switch (this.status_) {
+      case SafetyCheckExtensionsStatus.CHECKING:
+        return SafetyCheckIconStatus.RUNNING;
+      case SafetyCheckExtensionsStatus.ERROR:
+      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_ADMIN:
+        return SafetyCheckIconStatus.INFO;
+      case SafetyCheckExtensionsStatus.NO_BLOCKLISTED_EXTENSIONS:
+      case SafetyCheckExtensionsStatus.BLOCKLISTED_ALL_DISABLED:
+        return SafetyCheckIconStatus.SAFE;
+      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_USER:
+      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_SOME_BY_USER:
+        return SafetyCheckIconStatus.WARNING;
+      default:
+        assertNotReached();
+    }
+  },
+
+  /**
+   * @private
+   * @return {?string}
+   */
+  getButtonLabel_: function() {
+    switch (this.status_) {
+      case SafetyCheckExtensionsStatus.BLOCKLISTED_ALL_DISABLED:
+      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_USER:
+      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_SOME_BY_USER:
+        return this.i18n('safetyCheckReview');
+      default:
+        return null;
+    }
+  },
+
+  /**
+   * @private
+   * @return {string}
+   */
+  getButtonClass_: function() {
+    switch (this.status_) {
+      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_USER:
+      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_SOME_BY_USER:
+        return 'action-button';
+      default:
+        return '';
+    }
+  },
+
+  /** @private */
+  onButtonClick_: function() {
+    // Log click both in action and histogram.
+    this.metricsBrowserProxy_.recordSafetyCheckInteractionHistogram(
+        SafetyCheckInteractions.SAFETY_CHECK_EXTENSIONS_REVIEW);
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.SafetyCheck.ReviewExtensions');
+
+    OpenWindowProxyImpl.getInstance().openURL('chrome://extensions');
+  },
+
+  /**
+   * @private
+   * @return {?string}
+   */
+  getManagedIcon_: function() {
+    switch (this.status_) {
+      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_ADMIN:
+        return 'cr20:domain';
+      default:
+        return null;
+    }
+  },
+});
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_page.html b/chrome/browser/resources/settings/safety_check_page/safety_check_page.html
index 0421d26..81df827 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_page.html
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_page.html
@@ -1,4 +1,3 @@
-
     <style include="cr-shared-style settings-shared iron-flex">
       #safetyCheckCollapse .list-item.selected {
          min-height: var(--settings-row-two-line-min-height);
@@ -10,25 +9,6 @@
         padding-inline-end: var(--cr-icon-button-margin-start);
         width: var(--cr-link-row-icon-width, var(--cr-icon-size));
       }
-
-      .icon-blue {
-        fill: var(--google-blue-600);
-      }
-
-      .icon-red {
-        fill: var(--google-red-600);
-      }
-
-      /* dark mode */
-      @media (prefers-color-scheme: dark) {
-        .icon-blue {
-          fill: var(--google-blue-refresh-300);
-        }
-
-        .icon-red {
-          fill: var(--google-red-refresh-300);
-        }
-      }
     </style>
     <div id="safetyCheckParent" class="cr-row first two-line">
       <iron-icon icon="settings20:safety-check" aria-hidden="true">
@@ -57,121 +37,12 @@
     </div>
     <iron-collapse id="safetyCheckCollapse"
         opened="[[shouldShowChildren_(parentStatus_)]]">
-      <div class="cr-row">
-        <iron-icon icon="[[getUpdatesIcon_(updatesStatus_)]]"
-            id="updatesIcon"
-            role="img"
-            aria-label="[[getUpdatesIconAriaLabel_(updatesStatus_)]]"
-            src="[[getUpdatesIconSrc_(updatesStatus_)]]"
-            class$="[[getUpdatesIconClass_(updatesStatus_)]]">
-        </iron-icon>
-        <div class="flex cr-padded-text">
-          <div>$i18n{safetyCheckUpdatesPrimaryLabel}</div>
-          <div class="secondary" no-search
-              inner-h-t-m-l="[[updatesDisplayString_]]">
-          </div>
-        </div>
-        <template is="dom-if"
-            if="[[shouldShowUpdatesButton_(updatesStatus_)]]" restamp>
-          <div class="separator"></div>
-          <cr-button id="safetyCheckUpdatesButton" class="action-button"
-              on-click="onSafetyCheckUpdatesButtonClick_" no-search
-            aria-label="$i18n{safetyCheckUpdatesButtonAriaLabel}">
-            $i18n{aboutRelaunch}
-          </cr-button>
-        </template>
-        <template is="dom-if"
-            if="[[shouldShowUpdatesManagedIcon_(updatesStatus_)]]" restamp>
-          <iron-icon id="safetyCheckUpdatesManagedIcon" icon="cr20:domain"
-              aria-hidden="true">
-          </iron-icon>
-        </template>
-      </div>
-      <div class="cr-row">
-        <iron-icon icon="[[getPasswordsIcon_(passwordsStatus_)]]"
-            id="passwordsIcon"
-            src="[[getPasswordsIconSrc_(passwordsStatus_)]]"
-            class$="[[getPasswordsIconClass_(passwordsStatus_)]]"
-            role="img"
-            aria-label="[[getPasswordsIconAriaLabel_(passwordsStatus_)]]">
-        </iron-icon>
-        <div class="flex cr-padded-text">
-          <div>$i18n{passwords}</div>
-          <div class="secondary" no-search
-              inner-h-t-m-l="[[passwordsDisplayString_]]">
-          </div>
-        </div>
-        <template is="dom-if"
-            if="[[shouldShowPasswordsButton_(passwordsStatus_)]]" restamp>
-          <div class="separator"></div>
-          <cr-button id="safetyCheckPasswordsButton" class="action-button"
-              on-click="onPasswordsButtonClick_" no-search
-              aria-label="$i18n{safetyCheckPasswordsButtonAriaLabel}">
-            $i18n{safetyCheckReview}
-          </cr-button>
-        </template>
-      </div>
-      <div class="cr-row">
-        <iron-icon icon="[[getSafeBrowsingIcon_(safeBrowsingStatus_)]]"
-            id="safeBrowsingIcon"
-            src="[[getSafeBrowsingIconSrc_(safeBrowsingStatus_)]]"
-            class$="[[getSafeBrowsingIconClass_(safeBrowsingStatus_)]]"
-            role="img"
-            aria-label="[[getSafeBrowsingIconAriaLabel_(safeBrowsingStatus_)]]">
-        </iron-icon>
-        <div class="flex cr-padded-text">
-          <div>$i18n{safeBrowsingSectionLabel}</div>
-          <div class="secondary" no-search
-              inner-h-t-m-l="[[safeBrowsingDisplayString_]]">
-          </div>
-        </div>
-        <template is="dom-if"
-            if="[[shouldShowSafeBrowsingButton_(safeBrowsingStatus_)]]" restamp>
-          <div class="separator"></div>
-          <cr-button id="safetyCheckSafeBrowsingButton" class="action-button"
-              on-click="onSafeBrowsingButtonClick_" no-search
-              aria-label="$i18n{safetyCheckSafeBrowsingButtonAriaLabel}">
-            $i18n{safetyCheckSafeBrowsingButton}
-          </cr-button>
-        </template>
-        <template is="dom-if"
-            if="[[shouldShowSafeBrowsingManagedIcon_(safeBrowsingStatus_)]]"
-            restamp>
-          <iron-icon id="safetyCheckSafeBrowsingManagedIcon" aria-hidden="true"
-              icon="[[getSafeBrowsingManagedIcon_(safeBrowsingStatus_)]]">
-          </iron-icon>
-        </template>
-      </div>
-      <div class="cr-row">
-        <iron-icon icon="[[getExtensionsIcon_(extensionsStatus_)]]"
-            id="extensionsIcon"
-            src="[[getExtensionsIconSrc_(extensionsStatus_)]]"
-            class$="[[getExtensionsIconClass_(extensionsStatus_)]]"
-            role="img"
-            aria-label="[[getExtensionsIconAriaLabel_(extensionsStatus_)]]">
-        </iron-icon>
-        <div class="flex cr-padded-text">
-          <div>$i18n{safetyCheckExtensionsPrimaryLabel}</div>
-          <div class="secondary" no-search
-              inner-h-t-m-l="[[extensionsDisplayString_]]">
-          </div>
-        </div>
-        <template is="dom-if"
-            if="[[shouldShowExtensionsButton_(extensionsStatus_)]]" restamp>
-          <div class="separator"></div>
-          <cr-button id="safetyCheckExtensionsButton"
-              class$="[[getExtensionsButtonClass_(extensionsStatus_)]]"
-              on-click="onSafetyCheckExtensionsButtonClick_" no-search
-              aria-label="$i18n{safetyCheckExtensionsButtonAriaLabel}">
-            $i18n{safetyCheckReview}
-          </cr-button>
-        </template>
-        <template is="dom-if"
-            if="[[shouldShowExtensionsManagedIcon_(extensionsStatus_)]]"
-            restamp>
-          <iron-icon id="safetyCheckExtensionsManagedIcon" icon="cr20:domain"
-              aria-hidden="true">
-          </iron-icon>
-        </template>
-      </div>
+      <settings-safety-check-updates-element>
+      </settings-safety-check-updates-element>
+      <settings-safety-check-passwords-element>
+      </settings-safety-check-passwords-element>
+      <settings-safety-check-safe-browsing-element>
+      </settings-safety-check-safe-browsing-element>
+      <settings-safety-check-extensions-element>
+      </settings-safety-check-extensions-element>
     </iron-collapse>
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_page.js b/chrome/browser/resources/settings/safety_check_page/safety_check_page.js
index 4d6d1172..0266ba4 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_page.js
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_page.js
@@ -16,6 +16,10 @@
 import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import '../settings_shared_css.m.js';
+import './safety_check_extensions_element.js';
+import './safety_check_passwords_element.js';
+import './safety_check_safe_browsing_element.js';
+import './safety_check_updates_element.js';
 
 import {assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
@@ -23,29 +27,11 @@
 import {IronA11yAnnouncer} from 'chrome://resources/polymer/v3_0/iron-a11y-announcer/iron-a11y-announcer.js';
 import {flush, html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {PasswordManagerImpl, PasswordManagerProxy} from '../autofill_page/password_manager_proxy.js';
 import {HatsBrowserProxyImpl} from '../hats_browser_proxy.js';
 import {loadTimeData} from '../i18n_setup.js';
-import {LifetimeBrowserProxy, LifetimeBrowserProxyImpl} from '../lifetime_browser_proxy.m.js';
 import {MetricsBrowserProxy, MetricsBrowserProxyImpl, SafetyCheckInteractions} from '../metrics_browser_proxy.js';
-import {OpenWindowProxyImpl} from '../open_window_proxy.js';
-import {PrefsBehavior} from '../prefs/prefs_behavior.m.js';
-import {routes} from '../route.js';
-import {Router} from '../router.m.js';
 
-import {SafetyCheckBrowserProxy, SafetyCheckBrowserProxyImpl, SafetyCheckCallbackConstants, SafetyCheckExtensionsStatus, SafetyCheckParentStatus, SafetyCheckPasswordsStatus, SafetyCheckSafeBrowsingStatus, SafetyCheckUpdatesStatus} from './safety_check_browser_proxy.js';
-
-
-/**
- * UI states a safety check child can be in. Defines the basic UI of the child.
- * @enum {number}
- */
-const ChildUiStatus = {
-  RUNNING: 0,
-  SAFE: 1,
-  INFO: 2,
-  WARNING: 3,
-};
+import {SafetyCheckBrowserProxy, SafetyCheckBrowserProxyImpl, SafetyCheckCallbackConstants, SafetyCheckParentStatus} from './safety_check_browser_proxy.js';
 
 /**
  * @typedef {{
@@ -55,38 +41,6 @@
  */
 let ParentChangedEvent;
 
-/**
- * @typedef {{
- *   newState: SafetyCheckUpdatesStatus,
- *   displayString: string,
- * }}
- */
-let UpdatesChangedEvent;
-
-/**
- * @typedef {{
- *   newState: SafetyCheckPasswordsStatus,
- *   displayString: string,
- * }}
- */
-let PasswordsChangedEvent;
-
-/**
- * @typedef {{
- *   newState: SafetyCheckSafeBrowsingStatus,
- *   displayString: string,
- * }}
- */
-let SafeBrowsingChangedEvent;
-
-/**
- * @typedef {{
- *   newState: SafetyCheckExtensionsStatus,
- *   displayString: string,
- * }}
- */
-let ExtensionsChangedEvent;
-
 Polymer({
   is: 'settings-safety-check-page',
 
@@ -108,78 +62,15 @@
     },
 
     /**
-     * Current state of the safety check updates element.
-     * @private {!SafetyCheckUpdatesStatus}
-     */
-    updatesStatus_: {
-      type: Number,
-      value: SafetyCheckUpdatesStatus.CHECKING,
-    },
-
-    /**
-     * Current state of the safety check passwords element.
-     * @private {!SafetyCheckPasswordsStatus}
-     */
-    passwordsStatus_: {
-      type: Number,
-      value: SafetyCheckPasswordsStatus.CHECKING,
-    },
-
-    /**
-     * Current state of the safety check safe browsing element.
-     * @private {!SafetyCheckSafeBrowsingStatus}
-     */
-    safeBrowsingStatus_: {
-      type: Number,
-      value: SafetyCheckSafeBrowsingStatus.CHECKING,
-    },
-
-    /**
-     * Current state of the safety check extensions element.
-     * @private {!SafetyCheckExtensionsStatus}
-     */
-    extensionsStatus_: {
-      type: Number,
-      value: SafetyCheckExtensionsStatus.CHECKING,
-    },
-
-    /**
      * UI string to display for the parent status.
      * @private
      */
     parentDisplayString_: String,
-
-    /**
-     * UI string to display for the updates status.
-     * @private
-     */
-    updatesDisplayString_: String,
-
-    /**
-     * UI string to display for the passwords status.
-     * @private
-     */
-    passwordsDisplayString_: String,
-
-    /**
-     * UI string to display for the Safe Browsing status.
-     * @private
-     */
-    safeBrowsingDisplayString_: String,
-
-    /**
-     * UI string to display for the extensions status.
-     * @private
-     */
-    extensionsDisplayString_: String,
   },
 
   /** @private {SafetyCheckBrowserProxy} */
   safetyCheckBrowserProxy_: null,
 
-  /** @private {?LifetimeBrowserProxy} */
-  lifetimeBrowserProxy_: null,
-
   /** @private {?MetricsBrowserProxy} */
   metricsBrowserProxy_: null,
 
@@ -192,25 +83,12 @@
   /** @override */
   attached: function() {
     this.safetyCheckBrowserProxy_ = SafetyCheckBrowserProxyImpl.getInstance();
-    this.lifetimeBrowserProxy_ = LifetimeBrowserProxyImpl.getInstance();
     this.metricsBrowserProxy_ = MetricsBrowserProxyImpl.getInstance();
 
     // Register for safety check status updates.
     this.addWebUIListener(
         SafetyCheckCallbackConstants.PARENT_CHANGED,
         this.onSafetyCheckParentChanged_.bind(this));
-    this.addWebUIListener(
-        SafetyCheckCallbackConstants.UPDATES_CHANGED,
-        this.onSafetyCheckUpdatesChanged_.bind(this));
-    this.addWebUIListener(
-        SafetyCheckCallbackConstants.PASSWORDS_CHANGED,
-        this.onSafetyCheckPasswordsChanged_.bind(this));
-    this.addWebUIListener(
-        SafetyCheckCallbackConstants.SAFE_BROWSING_CHANGED,
-        this.onSafetyCheckSafeBrowsingChanged_.bind(this));
-    this.addWebUIListener(
-        SafetyCheckCallbackConstants.EXTENSIONS_CHANGED,
-        this.onSafetyCheckExtensionsChanged_.bind(this));
 
     // Configure default UI.
     this.parentDisplayString_ =
@@ -260,42 +138,6 @@
   },
 
   /**
-   * @param {!UpdatesChangedEvent} event
-   * @private
-   */
-  onSafetyCheckUpdatesChanged_: function(event) {
-    this.updatesStatus_ = event.newState;
-    this.updatesDisplayString_ = event.displayString;
-  },
-
-  /**
-   * @param {!PasswordsChangedEvent} event
-   * @private
-   */
-  onSafetyCheckPasswordsChanged_: function(event) {
-    this.passwordsDisplayString_ = event.displayString;
-    this.passwordsStatus_ = event.newState;
-  },
-
-  /**
-   * @param {!SafeBrowsingChangedEvent} event
-   * @private
-   */
-  onSafetyCheckSafeBrowsingChanged_: function(event) {
-    this.safeBrowsingStatus_ = event.newState;
-    this.safeBrowsingDisplayString_ = event.displayString;
-  },
-
-  /**
-   * @param {!ExtensionsChangedEvent} event
-   * @private
-   */
-  onSafetyCheckExtensionsChanged_: function(event) {
-    this.extensionsDisplayString_ = event.displayString;
-    this.extensionsStatus_ = event.newState;
-  },
-
-  /**
    * @private
    * @return {boolean}
    */
@@ -338,438 +180,4 @@
   shouldShowChildren_: function() {
     return this.parentStatus_ != SafetyCheckParentStatus.BEFORE;
   },
-
-  /**
-   * Returns the default icon for a safety check child in the specified state.
-   * @private
-   * @param {ChildUiStatus} childUiStatus
-   * @return {?string}
-   */
-  getChildUiIcon_: function(childUiStatus) {
-    switch (childUiStatus) {
-      case ChildUiStatus.RUNNING:
-        return null;
-      case ChildUiStatus.SAFE:
-        return 'cr:check';
-      case ChildUiStatus.INFO:
-        return 'cr:info';
-      case ChildUiStatus.WARNING:
-        return 'cr:warning';
-      default:
-        assertNotReached();
-    }
-  },
-
-  /**
-   * Returns the default icon src for animated icons for a safety check child
-   * in the specified state.
-   * @private
-   * @param {ChildUiStatus} childUiStatus
-   * @return {?string}
-   */
-  getChildUiIconSrc_: function(childUiStatus) {
-    if (childUiStatus === ChildUiStatus.RUNNING) {
-      return 'chrome://resources/images/throbber_small.svg';
-    }
-    return null;
-  },
-
-  /**
-   * Returns the default icon class for a safety check child in the specified
-   * state.
-   * @private
-   * @param {ChildUiStatus} childUiStatus
-   * @return {string}
-   */
-  getChildUiIconClass_: function(childUiStatus) {
-    switch (childUiStatus) {
-      case ChildUiStatus.RUNNING:
-      case ChildUiStatus.SAFE:
-        return 'icon-blue';
-      case ChildUiStatus.WARNING:
-        return 'icon-red';
-      default:
-        return '';
-    }
-  },
-
-  /**
-   * Returns the default icon aria label for a safety check child in the
-   * specified state.
-   * @private
-   * @param {ChildUiStatus} childUiStatus
-   * @return {string}
-   */
-  getChildUiIconAriaLabel_: function(childUiStatus) {
-    switch (childUiStatus) {
-      case ChildUiStatus.RUNNING:
-        return this.i18n('safetyCheckIconRunningAriaLabel');
-      case ChildUiStatus.SAFE:
-        return this.i18n('safetyCheckIconSafeAriaLabel');
-      case ChildUiStatus.INFO:
-        return this.i18n('safetyCheckIconInfoAriaLabel');
-      case ChildUiStatus.WARNING:
-        return this.i18n('safetyCheckIconWarningAriaLabel');
-      default:
-        assertNotReached();
-    }
-  },
-
-  /**
-   * @private
-   * @return {boolean}
-   */
-  shouldShowUpdatesButton_: function() {
-    return this.updatesStatus_ === SafetyCheckUpdatesStatus.RELAUNCH;
-  },
-
-  /**
-   * @private
-   * @return {boolean}
-   */
-  shouldShowUpdatesManagedIcon_: function() {
-    return this.updatesStatus_ === SafetyCheckUpdatesStatus.DISABLED_BY_ADMIN;
-  },
-
-  /** @private */
-  onSafetyCheckUpdatesButtonClick_: function() {
-    // Log click both in action and histogram.
-    this.metricsBrowserProxy_.recordSafetyCheckInteractionHistogram(
-        SafetyCheckInteractions.SAFETY_CHECK_UPDATES_RELAUNCH);
-    this.metricsBrowserProxy_.recordAction(
-        'Settings.SafetyCheck.RelaunchAfterUpdates');
-
-    this.lifetimeBrowserProxy_.relaunch();
-  },
-
-  /**
-   * @return {ChildUiStatus}
-   * @private
-   */
-  getUpdatesUiStatus_: function() {
-    switch (this.updatesStatus_) {
-      case SafetyCheckUpdatesStatus.CHECKING:
-      case SafetyCheckUpdatesStatus.UPDATING:
-        return ChildUiStatus.RUNNING;
-      case SafetyCheckUpdatesStatus.UPDATED:
-        return ChildUiStatus.SAFE;
-      case SafetyCheckUpdatesStatus.RELAUNCH:
-      case SafetyCheckUpdatesStatus.DISABLED_BY_ADMIN:
-      case SafetyCheckUpdatesStatus.FAILED_OFFLINE:
-      case SafetyCheckUpdatesStatus.UNKNOWN:
-        return ChildUiStatus.INFO;
-      case SafetyCheckUpdatesStatus.FAILED:
-        return ChildUiStatus.WARNING;
-      default:
-        assertNotReached();
-    }
-  },
-
-  /**
-   * @private
-   * @return {?string}
-   */
-  getUpdatesIcon_: function() {
-    return this.getChildUiIcon_(this.getUpdatesUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {?string}
-   */
-  getUpdatesIconSrc_: function() {
-    return this.getChildUiIconSrc_(this.getUpdatesUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {string}
-   */
-  getUpdatesIconClass_: function() {
-    return this.getChildUiIconClass_(this.getUpdatesUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {string}
-   */
-  getUpdatesIconAriaLabel_: function() {
-    return this.getChildUiIconAriaLabel_(this.getUpdatesUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {boolean}
-   */
-  shouldShowPasswordsButton_: function() {
-    return this.passwordsStatus_ === SafetyCheckPasswordsStatus.COMPROMISED;
-  },
-
-  /**
-   * @private
-   * @return {ChildUiStatus}
-   */
-  getPasswordsUiStatus_: function() {
-    switch (this.passwordsStatus_) {
-      case SafetyCheckPasswordsStatus.CHECKING:
-        return ChildUiStatus.RUNNING;
-      case SafetyCheckPasswordsStatus.SAFE:
-        return ChildUiStatus.SAFE;
-      case SafetyCheckPasswordsStatus.COMPROMISED:
-        return ChildUiStatus.WARNING;
-      case SafetyCheckPasswordsStatus.OFFLINE:
-      case SafetyCheckPasswordsStatus.NO_PASSWORDS:
-      case SafetyCheckPasswordsStatus.SIGNED_OUT:
-      case SafetyCheckPasswordsStatus.QUOTA_LIMIT:
-      case SafetyCheckPasswordsStatus.ERROR:
-        return ChildUiStatus.INFO;
-      default:
-        assertNotReached();
-    }
-  },
-
-  /**
-   * @private
-   * @return {?string}
-   */
-  getPasswordsIcon_: function() {
-    return this.getChildUiIcon_(this.getPasswordsUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {?string}
-   */
-  getPasswordsIconSrc_: function() {
-    return this.getChildUiIconSrc_(this.getPasswordsUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {string}
-   */
-  getPasswordsIconClass_: function() {
-    return this.getChildUiIconClass_(this.getPasswordsUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {string}
-   */
-  getPasswordsIconAriaLabel_: function() {
-    return this.getChildUiIconAriaLabel_(this.getPasswordsUiStatus_());
-  },
-
-  /** @private */
-  onPasswordsButtonClick_: function() {
-    // Log click both in action and histogram.
-    this.metricsBrowserProxy_.recordSafetyCheckInteractionHistogram(
-        SafetyCheckInteractions.SAFETY_CHECK_PASSWORDS_MANAGE);
-    this.metricsBrowserProxy_.recordAction(
-        'Settings.SafetyCheck.ManagePasswords');
-
-    Router.getInstance().navigateTo(routes.CHECK_PASSWORDS);
-    PasswordManagerImpl.getInstance().recordPasswordCheckReferrer(
-        PasswordManagerProxy.PasswordCheckReferrer.SAFETY_CHECK);
-  },
-
-  /**
-   * @private
-   * @return {boolean}
-   */
-  shouldShowSafeBrowsingButton_: function() {
-    return this.safeBrowsingStatus_ === SafetyCheckSafeBrowsingStatus.DISABLED;
-  },
-
-  /**
-   * @private
-   * @return {boolean}
-   */
-  shouldShowSafeBrowsingManagedIcon_: function() {
-    return this.getSafeBrowsingManagedIcon_() != null;
-  },
-
-  /**
-   * @private
-   * @return {?string}
-   */
-  getSafeBrowsingManagedIcon_: function() {
-    switch (this.safeBrowsingStatus_) {
-      case SafetyCheckSafeBrowsingStatus.DISABLED_BY_ADMIN:
-        return 'cr20:domain';
-      case SafetyCheckSafeBrowsingStatus.DISABLED_BY_EXTENSION:
-        return 'cr:extension';
-      default:
-        return null;
-    }
-  },
-
-  /**
-   * @private
-   * @return {ChildUiStatus}
-   */
-  getSafeBrowsingUiStatus_: function() {
-    switch (this.safeBrowsingStatus_) {
-      case SafetyCheckSafeBrowsingStatus.CHECKING:
-        return ChildUiStatus.RUNNING;
-      case SafetyCheckSafeBrowsingStatus.ENABLED_STANDARD:
-      case SafetyCheckSafeBrowsingStatus.ENABLED_ENHANCED:
-        return ChildUiStatus.SAFE;
-      case SafetyCheckSafeBrowsingStatus.ENABLED:
-        // ENABLED is deprecated.
-        assertNotReached();
-      case SafetyCheckSafeBrowsingStatus.DISABLED:
-      case SafetyCheckSafeBrowsingStatus.DISABLED_BY_ADMIN:
-      case SafetyCheckSafeBrowsingStatus.DISABLED_BY_EXTENSION:
-        return ChildUiStatus.INFO;
-      default:
-        assertNotReached();
-    }
-  },
-
-  /**
-   * @private
-   * @return {?string}
-   */
-  getSafeBrowsingIcon_: function() {
-    return this.getChildUiIcon_(this.getSafeBrowsingUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {?string}
-   */
-  getSafeBrowsingIconSrc_: function() {
-    return this.getChildUiIconSrc_(this.getSafeBrowsingUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {string}
-   */
-  getSafeBrowsingIconClass_: function() {
-    return this.getChildUiIconClass_(this.getSafeBrowsingUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {string}
-   */
-  getSafeBrowsingIconAriaLabel_: function() {
-    return this.getChildUiIconAriaLabel_(this.getSafeBrowsingUiStatus_());
-  },
-
-  /** @private */
-  onSafeBrowsingButtonClick_: function() {
-    // Log click both in action and histogram.
-    this.metricsBrowserProxy_.recordSafetyCheckInteractionHistogram(
-        SafetyCheckInteractions.SAFETY_CHECK_SAFE_BROWSING_MANAGE);
-    this.metricsBrowserProxy_.recordAction(
-        'Settings.SafetyCheck.ManageSafeBrowsing');
-
-    Router.getInstance().navigateTo(routes.SECURITY);
-  },
-
-  /**
-   * @private
-   * @return {boolean}
-   */
-  shouldShowExtensionsButton_: function() {
-    switch (this.extensionsStatus_) {
-      case SafetyCheckExtensionsStatus.BLOCKLISTED_ALL_DISABLED:
-      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_USER:
-      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_SOME_BY_USER:
-        return true;
-      default:
-        return false;
-    }
-  },
-
-  /**
-   * @private
-   * @return {boolean}
-   */
-  shouldShowExtensionsManagedIcon_: function() {
-    return this.extensionsStatus_ ===
-        SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_ADMIN;
-  },
-
-  /** @private */
-  onSafetyCheckExtensionsButtonClick_: function() {
-    // Log click both in action and histogram.
-    this.metricsBrowserProxy_.recordSafetyCheckInteractionHistogram(
-        SafetyCheckInteractions.SAFETY_CHECK_EXTENSIONS_REVIEW);
-    this.metricsBrowserProxy_.recordAction(
-        'Settings.SafetyCheck.ReviewExtensions');
-
-    OpenWindowProxyImpl.getInstance().openURL('chrome://extensions');
-  },
-
-  /**
-   * @private
-   * @return {ChildUiStatus}
-   */
-  getExtensionsUiStatus_: function() {
-    switch (this.extensionsStatus_) {
-      case SafetyCheckExtensionsStatus.CHECKING:
-        return ChildUiStatus.RUNNING;
-      case SafetyCheckExtensionsStatus.ERROR:
-      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_ADMIN:
-        return ChildUiStatus.INFO;
-      case SafetyCheckExtensionsStatus.NO_BLOCKLISTED_EXTENSIONS:
-      case SafetyCheckExtensionsStatus.BLOCKLISTED_ALL_DISABLED:
-        return ChildUiStatus.SAFE;
-      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_USER:
-      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_SOME_BY_USER:
-        return ChildUiStatus.WARNING;
-      default:
-        assertNotReached();
-    }
-  },
-
-  /**
-   * @private
-   * @return {?string}
-   */
-  getExtensionsIcon_: function() {
-    return this.getChildUiIcon_(this.getExtensionsUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {?string}
-   */
-  getExtensionsIconSrc_: function() {
-    return this.getChildUiIconSrc_(this.getExtensionsUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {string}
-   */
-  getExtensionsIconClass_: function() {
-    return this.getChildUiIconClass_(this.getExtensionsUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {string}
-   */
-  getExtensionsIconAriaLabel_: function() {
-    return this.getChildUiIconAriaLabel_(this.getExtensionsUiStatus_());
-  },
-
-  /**
-   * @private
-   * @return {string}
-   */
-  getExtensionsButtonClass_: function() {
-    switch (this.extensionsStatus_) {
-      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_USER:
-      case SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_SOME_BY_USER:
-        return 'action-button';
-      default:
-        return '';
-    }
-  },
 });
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_element.html b/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_element.html
new file mode 100644
index 0000000..55bbdb06
--- /dev/null
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_element.html
@@ -0,0 +1,10 @@
+<settings-safety-check-child
+  id="safetyCheckChild"
+  icon-status="[[getIconStatus_(status_)]]"
+  label="$i18n{passwords}"
+  sub-label="[[displayString_]]"
+  button-label="[[getButtonLabel_(status_)]]"
+  button-aria-label="$i18n{safetyCheckPasswordsButtonAriaLabel}"
+  button-class="action-button"
+  on-button-click="onButtonClick_">
+</settings-safety-check-child>
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_element.js b/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_element.js
new file mode 100644
index 0000000..790780e
--- /dev/null
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_element.js
@@ -0,0 +1,125 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'settings-safety-passwords-element' is the settings page containing the
+ * safety check element showing the password status.
+ */
+import {assertNotReached} from 'chrome://resources/js/assert.m.js';
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {PasswordManagerImpl, PasswordManagerProxy} from '../autofill_page/password_manager_proxy.js';
+import {MetricsBrowserProxy, MetricsBrowserProxyImpl, SafetyCheckInteractions} from '../metrics_browser_proxy.js';
+import {routes} from '../route.js';
+import {Router} from '../router.m.js';
+
+import {SafetyCheckCallbackConstants, SafetyCheckPasswordsStatus} from './safety_check_browser_proxy.js';
+import {SafetyCheckIconStatus} from './safety_check_child.js';
+
+/**
+ * @typedef {{
+ *   newState: SafetyCheckPasswordsStatus,
+ *   displayString: string,
+ * }}
+ */
+let PasswordsChangedEvent;
+
+Polymer({
+  is: 'settings-safety-check-passwords-element',
+
+  _template: html`{__html_template__}`,
+
+  behaviors: [
+    I18nBehavior,
+    WebUIListenerBehavior,
+  ],
+
+  properties: {
+    /**
+     * Current state of the safety check passwords element.
+     * @private {!SafetyCheckPasswordsStatus}
+     */
+    status_: {
+      type: Number,
+      value: SafetyCheckPasswordsStatus.CHECKING,
+    },
+
+    /** UI string to display for this child, received from the backend. */
+    displayString_: String,
+  },
+
+  /** ?MetricsBrowserProxy */
+  metricsBrowserProxy_: null,
+
+  /** @override */
+  attached: function() {
+    this.metricsBrowserProxy_ = MetricsBrowserProxyImpl.getInstance();
+
+    // Register for safety check status updates.
+    this.addWebUIListener(
+        SafetyCheckCallbackConstants.PASSWORDS_CHANGED,
+        this.onSafetyCheckPasswordsChanged_.bind(this));
+  },
+
+  /**
+   * @param {!PasswordsChangedEvent} event
+   * @private
+   */
+  onSafetyCheckPasswordsChanged_: function(event) {
+    this.status_ = event.newState;
+    this.displayString_ = event.displayString;
+  },
+
+  /**
+   * @return {SafetyCheckIconStatus}
+   * @private
+   */
+  getIconStatus_: function() {
+    switch (this.status_) {
+      case SafetyCheckPasswordsStatus.CHECKING:
+        return SafetyCheckIconStatus.RUNNING;
+      case SafetyCheckPasswordsStatus.SAFE:
+        return SafetyCheckIconStatus.SAFE;
+      case SafetyCheckPasswordsStatus.COMPROMISED:
+        return SafetyCheckIconStatus.WARNING;
+      case SafetyCheckPasswordsStatus.OFFLINE:
+      case SafetyCheckPasswordsStatus.NO_PASSWORDS:
+      case SafetyCheckPasswordsStatus.SIGNED_OUT:
+      case SafetyCheckPasswordsStatus.QUOTA_LIMIT:
+      case SafetyCheckPasswordsStatus.ERROR:
+        return SafetyCheckIconStatus.INFO;
+      default:
+        assertNotReached();
+    }
+  },
+
+  /**
+   * @private
+   * @return {?string}
+   */
+  getButtonLabel_: function() {
+    switch (this.status_) {
+      case SafetyCheckPasswordsStatus.COMPROMISED:
+        return this.i18n('safetyCheckReview');
+      default:
+        return null;
+    }
+  },
+
+  /** @private */
+  onButtonClick_: function() {
+    // Log click both in action and histogram.
+    this.metricsBrowserProxy_.recordSafetyCheckInteractionHistogram(
+        SafetyCheckInteractions.SAFETY_CHECK_PASSWORDS_MANAGE);
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.SafetyCheck.ManagePasswords');
+
+    Router.getInstance().navigateTo(routes.CHECK_PASSWORDS);
+    PasswordManagerImpl.getInstance().recordPasswordCheckReferrer(
+        PasswordManagerProxy.PasswordCheckReferrer.SAFETY_CHECK);
+  },
+});
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_element.html b/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_element.html
new file mode 100644
index 0000000..9ed22b8a
--- /dev/null
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_element.html
@@ -0,0 +1,11 @@
+<settings-safety-check-child
+  id="safetyCheckChild"
+  icon-status="[[getIconStatus_(status_)]]"
+  label="$i18n{safeBrowsingSectionLabel}"
+  sub-label="[[displayString_]]"
+  button-label="[[getButtonLabel_(status_)]]"
+  button-aria-label="$i18n{safetyCheckSafeBrowsingButtonAriaLabel}"
+  button-class="action-button"
+  on-button-click="onButtonClick_"
+  managed-icon="[[getManagedIcon_(status_)]]">
+</settings-safety-check-child>
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_element.js b/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_element.js
new file mode 100644
index 0000000..73ff0c42
--- /dev/null
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_element.js
@@ -0,0 +1,138 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'settings-safety-safe-browsing-element' is the settings page containing the
+ * safety check element showing the Safe Browsing status.
+ */
+import {assertNotReached} from 'chrome://resources/js/assert.m.js';
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {MetricsBrowserProxy, MetricsBrowserProxyImpl, SafetyCheckInteractions} from '../metrics_browser_proxy.js';
+import {routes} from '../route.js';
+import {Router} from '../router.m.js';
+
+import {SafetyCheckCallbackConstants, SafetyCheckSafeBrowsingStatus} from './safety_check_browser_proxy.js';
+import {SafetyCheckIconStatus} from './safety_check_child.js';
+
+/**
+ * @typedef {{
+ *   newState: SafetyCheckSafeBrowsingStatus,
+ *   displayString: string,
+ * }}
+ */
+let SafeBrowsingChangedEvent;
+
+Polymer({
+  is: 'settings-safety-check-safe-browsing-element',
+
+  _template: html`{__html_template__}`,
+
+  behaviors: [
+    I18nBehavior,
+    WebUIListenerBehavior,
+  ],
+
+  properties: {
+    /**
+     * Current state of the safety check safe browsing element, received from
+     * the backend.
+     * @private {!SafetyCheckSafeBrowsingStatus}
+     */
+    status_: {
+      type: Number,
+      value: SafetyCheckSafeBrowsingStatus.CHECKING,
+    },
+
+    /** UI string to display for this child, received from the backend. */
+    displayString_: String,
+  },
+
+  /** ?MetricsBrowserProxy */
+  metricsBrowserProxy_: null,
+
+  /** @override */
+  attached: function() {
+    this.metricsBrowserProxy_ = MetricsBrowserProxyImpl.getInstance();
+
+    // Register for safety check status updates.
+    this.addWebUIListener(
+        SafetyCheckCallbackConstants.SAFE_BROWSING_CHANGED,
+        this.onSafetyCheckSafeBrowsingChanged_.bind(this));
+  },
+
+  /**
+   * @param {!SafeBrowsingChangedEvent} event
+   * @private
+   */
+  onSafetyCheckSafeBrowsingChanged_: function(event) {
+    this.displayString_ = event.displayString;
+    this.status_ = event.newState;
+  },
+
+  /**
+   * @return {SafetyCheckIconStatus}
+   * @private
+   */
+  getIconStatus_: function() {
+    switch (this.status_) {
+      case SafetyCheckSafeBrowsingStatus.CHECKING:
+        return SafetyCheckIconStatus.RUNNING;
+      case SafetyCheckSafeBrowsingStatus.ENABLED_STANDARD:
+      case SafetyCheckSafeBrowsingStatus.ENABLED_ENHANCED:
+        return SafetyCheckIconStatus.SAFE;
+      case SafetyCheckSafeBrowsingStatus.ENABLED:
+        // ENABLED is deprecated.
+        assertNotReached();
+      case SafetyCheckSafeBrowsingStatus.DISABLED:
+      case SafetyCheckSafeBrowsingStatus.DISABLED_BY_ADMIN:
+      case SafetyCheckSafeBrowsingStatus.DISABLED_BY_EXTENSION:
+        return SafetyCheckIconStatus.INFO;
+      default:
+        assertNotReached();
+    }
+  },
+
+  /**
+   * @private
+   * @return {?string}
+   */
+  getButtonLabel_: function() {
+    switch (this.status_) {
+      case SafetyCheckSafeBrowsingStatus.DISABLED:
+        return this.i18n('safetyCheckSafeBrowsingButton');
+      default:
+        return null;
+    }
+  },
+
+  /** @private */
+  onButtonClick_: function() {
+    // Log click both in action and histogram.
+    this.metricsBrowserProxy_.recordSafetyCheckInteractionHistogram(
+        SafetyCheckInteractions.SAFETY_CHECK_SAFE_BROWSING_MANAGE);
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.SafetyCheck.ManageSafeBrowsing');
+
+    Router.getInstance().navigateTo(routes.SECURITY);
+  },
+
+  /**
+   * @private
+   * @return {?string}
+   */
+  getManagedIcon_: function() {
+    switch (this.status_) {
+      case SafetyCheckSafeBrowsingStatus.DISABLED_BY_ADMIN:
+        return 'cr20:domain';
+      case SafetyCheckSafeBrowsingStatus.DISABLED_BY_EXTENSION:
+        return 'cr:extension';
+      default:
+        return null;
+    }
+  },
+});
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_updates_element.html b/chrome/browser/resources/settings/safety_check_page/safety_check_updates_element.html
new file mode 100644
index 0000000..9b247b6
--- /dev/null
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_updates_element.html
@@ -0,0 +1,11 @@
+<settings-safety-check-child
+  id="safetyCheckChild"
+  icon-status="[[getIconStatus_(status_)]]"
+  label="$i18n{safetyCheckUpdatesPrimaryLabel}"
+  sub-label="[[displayString_]]"
+  button-label="[[getButtonLabel_(status_)]]"
+  button-aria-label="$i18n{safetyCheckUpdatesButtonAriaLabel}"
+  button-class="action-button"
+  on-button-click="onButtonClick_"
+  managed-icon="[[getManagedIcon_(status_)]]">
+</settings-safety-check-child>
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_updates_element.js b/chrome/browser/resources/settings/safety_check_page/safety_check_updates_element.js
new file mode 100644
index 0000000..9d952fc
--- /dev/null
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_updates_element.js
@@ -0,0 +1,138 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'settings-safety-updates-element' is the settings page containing the safety
+ * check element showing the browser's update status.
+ */
+import {assertNotReached} from 'chrome://resources/js/assert.m.js';
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {LifetimeBrowserProxy, LifetimeBrowserProxyImpl} from '../lifetime_browser_proxy.m.js';
+import {MetricsBrowserProxy, MetricsBrowserProxyImpl, SafetyCheckInteractions} from '../metrics_browser_proxy.js';
+
+import {SafetyCheckCallbackConstants, SafetyCheckUpdatesStatus} from './safety_check_browser_proxy.js';
+import {SafetyCheckIconStatus} from './safety_check_child.js';
+
+/**
+ * @typedef {{
+ *   newState: SafetyCheckUpdatesStatus,
+ *   displayString: string,
+ * }}
+ */
+let UpdatesChangedEvent;
+
+Polymer({
+  is: 'settings-safety-check-updates-element',
+
+  _template: html`{__html_template__}`,
+
+  behaviors: [
+    I18nBehavior,
+    WebUIListenerBehavior,
+  ],
+
+  properties: {
+    /**
+     * Current state of the safety check updates element.
+     * @private {!SafetyCheckUpdatesStatus}
+     */
+    status_: {
+      type: Number,
+      value: SafetyCheckUpdatesStatus.CHECKING,
+    },
+
+    /** UI string to display for this child, received from the backend. */
+    displayString_: String,
+  },
+
+  /** @private {?LifetimeBrowserProxy} */
+  lifetimeBrowserProxy_: null,
+
+  /** ?MetricsBrowserProxy */
+  metricsBrowserProxy_: null,
+
+  /** @override */
+  attached: function() {
+    this.lifetimeBrowserProxy_ = LifetimeBrowserProxyImpl.getInstance();
+    this.metricsBrowserProxy_ = MetricsBrowserProxyImpl.getInstance();
+
+    // Register for safety check status updates.
+    this.addWebUIListener(
+        SafetyCheckCallbackConstants.UPDATES_CHANGED,
+        this.onSafetyCheckUpdatesChanged_.bind(this));
+  },
+
+  /**
+   * @param {!UpdatesChangedEvent} event
+   * @private
+   */
+  onSafetyCheckUpdatesChanged_: function(event) {
+    this.status_ = event.newState;
+    this.displayString_ = event.displayString;
+  },
+
+  /**
+   * @return {SafetyCheckIconStatus}
+   * @private
+   */
+  getIconStatus_: function() {
+    switch (this.status_) {
+      case SafetyCheckUpdatesStatus.CHECKING:
+      case SafetyCheckUpdatesStatus.UPDATING:
+        return SafetyCheckIconStatus.RUNNING;
+      case SafetyCheckUpdatesStatus.UPDATED:
+        return SafetyCheckIconStatus.SAFE;
+      case SafetyCheckUpdatesStatus.RELAUNCH:
+      case SafetyCheckUpdatesStatus.DISABLED_BY_ADMIN:
+      case SafetyCheckUpdatesStatus.FAILED_OFFLINE:
+      case SafetyCheckUpdatesStatus.UNKNOWN:
+        return SafetyCheckIconStatus.INFO;
+      case SafetyCheckUpdatesStatus.FAILED:
+        return SafetyCheckIconStatus.WARNING;
+      default:
+        assertNotReached();
+    }
+  },
+
+  /**
+   * @private
+   * @return {?string}
+   */
+  getButtonLabel_: function() {
+    switch (this.status_) {
+      case SafetyCheckUpdatesStatus.RELAUNCH:
+        return this.i18n('aboutRelaunch');
+      default:
+        return null;
+    }
+  },
+
+  /** @private */
+  onButtonClick_: function() {
+    // Log click both in action and histogram.
+    this.metricsBrowserProxy_.recordSafetyCheckInteractionHistogram(
+        SafetyCheckInteractions.SAFETY_CHECK_UPDATES_RELAUNCH);
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.SafetyCheck.RelaunchAfterUpdates');
+
+    this.lifetimeBrowserProxy_.relaunch();
+  },
+
+  /**
+   * @private
+   * @return {?string}
+   */
+  getManagedIcon_: function() {
+    switch (this.status_) {
+      case SafetyCheckUpdatesStatus.DISABLED_BY_ADMIN:
+        return 'cr20:domain';
+      default:
+        return null;
+    }
+  },
+});
diff --git a/chrome/browser/resources/settings/settings.gni b/chrome/browser/resources/settings/settings.gni
index f5bbb72d..48e79e5 100644
--- a/chrome/browser/resources/settings/settings.gni
+++ b/chrome/browser/resources/settings/settings.gni
@@ -98,6 +98,7 @@
   "settings.SafetyCheckBrowserProxy|SafetyCheckBrowserProxy",
   "settings.SafetyCheckCallbackConstants|SafetyCheckCallbackConstants",
   "settings.SafetyCheckExtensionsStatus|SafetyCheckExtensionsStatus",
+  "settings.SafetyCheckIconStatus|SafetyCheckIconStatus",
   "settings.SafetyCheckParentStatus|SafetyCheckParentStatus",
   "settings.SafetyCheckPasswordsStatus|SafetyCheckPasswordsStatus",
   "settings.SafetyCheckSafeBrowsingStatus|SafetyCheckSafeBrowsingStatus",
diff --git a/chrome/browser/resources/settings/settings.js b/chrome/browser/resources/settings/settings.js
index 6d6b929..e39f075 100644
--- a/chrome/browser/resources/settings/settings.js
+++ b/chrome/browser/resources/settings/settings.js
@@ -33,5 +33,6 @@
 export {buildRouter, routes} from './route.js';
 export {Route, Router} from './router.m.js';
 export {SafetyCheckBrowserProxy, SafetyCheckBrowserProxyImpl, SafetyCheckCallbackConstants, SafetyCheckExtensionsStatus, SafetyCheckParentStatus, SafetyCheckPasswordsStatus, SafetyCheckSafeBrowsingStatus, SafetyCheckUpdatesStatus} from './safety_check_page/safety_check_browser_proxy.js';
+export {SafetyCheckIconStatus} from './safety_check_page/safety_check_child.js';
 export {SearchEngine, SearchEnginesBrowserProxy, SearchEnginesBrowserProxyImpl, SearchEnginesInfo} from './search_engines_page/search_engines_browser_proxy.m.js';
 export {getSearchManager, SearchRequest, setSearchManagerForTesting} from './search_settings.m.js';
diff --git a/chrome/browser/resources/settings/settings_resources_v3.grdp b/chrome/browser/resources/settings/settings_resources_v3.grdp
index a920b76..4953d88 100644
--- a/chrome/browser/resources/settings/settings_resources_v3.grdp
+++ b/chrome/browser/resources/settings/settings_resources_v3.grdp
@@ -547,10 +547,30 @@
   <include name="IDR_SETTINGS_SAFETY_CHECK_PAGE_SAFETY_CHECK_BROWSER_PROXY_JS"
            file="safety_check_page/safety_check_browser_proxy.js"
            compress="false" type="BINDATA" />
+  <include name="IDR_SETTINGS_SAFETY_CHECK_PAGE_SAFETY_CHECK_CHILD_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/safety_check_page/safety_check_child.js"
+           use_base_dir="false"
+           compress="false" type="BINDATA" />
+  <include name="IDR_SETTINGS_SAFETY_CHECK_PAGE_SAFETY_CHECK_EXTENSIONS_ELEMENT_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/safety_check_page/safety_check_extensions_element.js"
+           use_base_dir="false"
+           compress="false" type="BINDATA" />
   <include name="IDR_SETTINGS_SAFETY_CHECK_PAGE_SAFETY_CHECK_PAGE_JS"
            file="${root_gen_dir}/chrome/browser/resources/settings/safety_check_page/safety_check_page.js"
            use_base_dir="false"
            compress="false" type="BINDATA" />
+  <include name="IDR_SETTINGS_SAFETY_CHECK_PAGE_SAFETY_CHECK_PASSWORDS_ELEMENT_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_element.js"
+           use_base_dir="false"
+           compress="false" type="BINDATA" />
+  <include name="IDR_SETTINGS_SAFETY_CHECK_PAGE_SAFETY_CHECK_SAFE_BROWSING_ELEMENT_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_element.js"
+           use_base_dir="false"
+           compress="false" type="BINDATA" />
+  <include name="IDR_SETTINGS_SAFETY_CHECK_PAGE_SAFETY_CHECK_UPDATES_ELEMENT_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/safety_check_page/safety_check_updates_element.js"
+           use_base_dir="false"
+           compress="false" type="BINDATA" />
   <include name="IDR_SETTINGS_SEARCH_ENGINES_BROWSER_PROXY_M_JS"
            file="${root_gen_dir}/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.m.js"
            use_base_dir="false"
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc
index 0f35c56..8b336eec 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.cc
@@ -223,7 +223,59 @@
 }
 
 views::View* DeepScanningDialogViews::GetContentsView() {
-  return contents_view_.get();
+  if (!contents_view_) {
+    contents_view_ = new views::View();  // Owned by caller.
+
+    // Create layout
+    views::GridLayout* layout =
+        contents_view_->SetLayoutManager(std::make_unique<views::GridLayout>());
+    views::ColumnSet* columns = layout->AddColumnSet(0);
+    columns->AddColumn(
+        /*h_align=*/views::GridLayout::FILL,
+        /*v_align=*/views::GridLayout::FILL,
+        /*resize_percent=*/1.0,
+        /*size_type=*/views::GridLayout::ColumnSize::kUsePreferred,
+        /*fixed_width=*/0,
+        /*min_width=*/0);
+
+    // Add the top image.
+    layout->StartRow(views::GridLayout::kFixedSize, 0);
+    image_ = layout->AddView(std::make_unique<DeepScanningTopImageView>(this));
+
+    // Add padding to distance the top image from the icon and message.
+    layout->AddPaddingRow(views::GridLayout::kFixedSize, 16);
+
+    // Add the side icon and message row.
+    layout->StartRow(views::GridLayout::kFixedSize, 0);
+    auto icon_and_message_row = std::make_unique<views::View>();
+    auto* row_layout = icon_and_message_row->SetLayoutManager(
+        std::make_unique<views::BoxLayout>(
+            views::BoxLayout::Orientation::kHorizontal,
+            kMessageAndIconRowInsets, kSideIconBetweenChildSpacing));
+    row_layout->set_main_axis_alignment(
+        views::BoxLayout::MainAxisAlignment::kStart);
+    row_layout->set_cross_axis_alignment(
+        views::BoxLayout::CrossAxisAlignment::kCenter);
+
+    // Add the side icon.
+    icon_and_message_row->AddChildView(CreateSideIcon());
+
+    // Add the message.
+    auto label = std::make_unique<DeepScanningMessageView>(this);
+    label->SetText(GetDialogMessage());
+    label->SetLineHeight(kLineHeight);
+    label->SetMultiLine(true);
+    label->SetVerticalAlignment(gfx::ALIGN_MIDDLE);
+    label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    message_ = icon_and_message_row->AddChildView(std::move(label));
+
+    layout->AddView(std::move(icon_and_message_row));
+
+    // Add padding to distance the message from the button(s).
+    layout->AddPaddingRow(views::GridLayout::kFixedSize, 10);
+  }
+
+  return contents_view_;
 }
 
 views::Widget* DeepScanningDialogViews::GetWidget() {
@@ -355,7 +407,7 @@
   if (is_success()) {
     DCHECK(contents_view_->parent());
     DCHECK_EQ(contents_view_->parent()->children().size(), 2ul);
-    DCHECK_EQ(contents_view_->parent()->children()[0], contents_view_.get());
+    DCHECK_EQ(contents_view_->parent()->children()[0], contents_view_);
 
     views::View* button_row_view = contents_view_->parent()->children()[1];
     new_height -= button_row_view->GetContentsBounds().height();
@@ -473,57 +525,6 @@
 
   SetupButtons();
 
-  contents_view_ = std::make_unique<views::View>();
-  contents_view_->set_owned_by_client();
-
-  // Create layout
-  views::GridLayout* layout =
-      contents_view_->SetLayoutManager(std::make_unique<views::GridLayout>());
-  views::ColumnSet* columns = layout->AddColumnSet(0);
-  columns->AddColumn(
-      /*h_align=*/views::GridLayout::FILL,
-      /*v_align=*/views::GridLayout::FILL,
-      /*resize_percent=*/1.0,
-      /*size_type=*/views::GridLayout::ColumnSize::kUsePreferred,
-      /*fixed_width=*/0,
-      /*min_width=*/0);
-
-  // Add the top image.
-  layout->StartRow(views::GridLayout::kFixedSize, 0);
-  image_ = layout->AddView(std::make_unique<DeepScanningTopImageView>(this));
-
-  // Add padding to distance the top image from the icon and message.
-  layout->AddPaddingRow(views::GridLayout::kFixedSize, 16);
-
-  // Add the side icon and message row.
-  layout->StartRow(views::GridLayout::kFixedSize, 0);
-  auto icon_and_message_row = std::make_unique<views::View>();
-  auto* row_layout =
-      icon_and_message_row->SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kHorizontal, kMessageAndIconRowInsets,
-          kSideIconBetweenChildSpacing));
-  row_layout->set_main_axis_alignment(
-      views::BoxLayout::MainAxisAlignment::kStart);
-  row_layout->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kCenter);
-
-  // Add the side icon.
-  icon_and_message_row->AddChildView(CreateSideIcon());
-
-  // Add the message.
-  auto label = std::make_unique<DeepScanningMessageView>(this);
-  label->SetText(GetDialogMessage());
-  label->SetLineHeight(kLineHeight);
-  label->SetMultiLine(true);
-  label->SetVerticalAlignment(gfx::ALIGN_MIDDLE);
-  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  message_ = icon_and_message_row->AddChildView(std::move(label));
-
-  layout->AddView(std::move(icon_and_message_row));
-
-  // Add padding to distance the message from the button(s).
-  layout->AddPaddingRow(views::GridLayout::kFixedSize, 10);
-
   constrained_window::ShowWebModalDialogViews(this, web_contents_);
 
   if (observer_for_testing)
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.h
index 9ebc2af..5e724a4e 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_views.h
@@ -216,7 +216,7 @@
   content::WebContents* web_contents_;
 
   // Views above the buttons. |contents_view_| owns every other view.
-  std::unique_ptr<views::View> contents_view_;
+  views::View* contents_view_ = nullptr;
   DeepScanningTopImageView* image_ = nullptr;
   DeepScanningSideIconImageView* side_icon_image_ = nullptr;
   DeepScanningSideIconSpinnerView* side_icon_spinner_ = nullptr;
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_metrics.cc b/chrome/browser/sharing/click_to_call/click_to_call_metrics.cc
index 6815e47..52f827b 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_metrics.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_metrics.cc
@@ -6,25 +6,12 @@
 
 #include <cctype>
 
-#include "base/metrics/histogram_functions.h"
-#include "base/time/time.h"
-#include "chrome/browser/sharing/click_to_call/click_to_call_utils.h"
 #include "components/ukm/content/source_url_recorder.h"
 #include "content/public/browser/web_contents.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
-ScopedUmaHistogramMicrosecondsTimer::ScopedUmaHistogramMicrosecondsTimer() =
-    default;
-
-ScopedUmaHistogramMicrosecondsTimer::~ScopedUmaHistogramMicrosecondsTimer() {
-  base::UmaHistogramCustomMicrosecondsTimes(
-      "Sharing.ClickToCallContextMenuPhoneNumberParsingDelay", timer_.Elapsed(),
-      base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(1),
-      /*buckets=*/50);
-}
-
 void LogClickToCallUKM(content::WebContents* web_contents,
                        SharingClickToCallEntryPoint entry_point,
                        bool has_devices,
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_metrics.h b/chrome/browser/sharing/click_to_call/click_to_call_metrics.h
index 39cbfd0..ee6382d 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_metrics.h
+++ b/chrome/browser/sharing/click_to_call/click_to_call_metrics.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_SHARING_CLICK_TO_CALL_CLICK_TO_CALL_METRICS_H_
 #define CHROME_BROWSER_SHARING_CLICK_TO_CALL_CLICK_TO_CALL_METRICS_H_
 
-#include "base/timer/elapsed_timer.h"
-
 namespace content {
 class WebContents;
 }  // namespace content
@@ -33,21 +31,6 @@
   kMaxValue = kApp,
 };
 
-// TODO(himanshujaju): Make it generic and move to base/metrics/histogram_base.h
-// Used to Log delay in parsing phone number in highlighted text to UMA.
-class ScopedUmaHistogramMicrosecondsTimer {
- public:
-  ScopedUmaHistogramMicrosecondsTimer();
-  ScopedUmaHistogramMicrosecondsTimer(
-      const ScopedUmaHistogramMicrosecondsTimer&) = delete;
-  ScopedUmaHistogramMicrosecondsTimer& operator=(
-      const ScopedUmaHistogramMicrosecondsTimer&) = delete;
-  ~ScopedUmaHistogramMicrosecondsTimer();
-
- private:
-  const base::ElapsedTimer timer_;
-};
-
 // Records a Click to Call selection to UKM. This is logged after a completed
 // action like selecting an app or a device to send the phone number to.
 void LogClickToCallUKM(content::WebContents* web_contents,
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_utils.cc b/chrome/browser/sharing/click_to_call/click_to_call_utils.cc
index ed2db195..07acb9e 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_utils.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_utils.cc
@@ -7,14 +7,10 @@
 #include <algorithm>
 #include <cctype>
 
-#include "base/metrics/histogram_functions.h"
 #include "base/optional.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "base/timer/elapsed_timer.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sharing/click_to_call/click_to_call_metrics.h"
 #include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/sharing/click_to_call/phone_number_regex.h"
 #include "chrome/browser/sharing/sharing_service.h"
@@ -78,7 +74,6 @@
 
 base::Optional<std::string> ExtractPhoneNumber(
     const std::string& selection_text) {
-  ScopedUmaHistogramMicrosecondsTimer scoped_uma_timer;
   std::string parsed_number;
 
   const re2::RE2& regex = GetPhoneNumberRegex();
diff --git a/chrome/browser/sharing/webrtc/sharing_mojo_service.cc b/chrome/browser/sharing/webrtc/sharing_mojo_service.cc
index 109dee4..cf251138 100644
--- a/chrome/browser/sharing/webrtc/sharing_mojo_service.cc
+++ b/chrome/browser/sharing/webrtc/sharing_mojo_service.cc
@@ -14,9 +14,7 @@
   content::ServiceProcessHost::Launch<mojom::Sharing>(
       remote.InitWithNewPipeAndPassReceiver(),
       content::ServiceProcessHost::Options()
-#if defined(OS_MACOSX)
-          .WithSandboxType(service_manager::SandboxType::kUtility)
-#else
+#if !defined(OS_MACOSX)
           .WithSandboxType(service_manager::SandboxType::kSharingService)
 #endif
           .WithDisplayName("Sharing Service")
diff --git a/chrome/browser/speech/speech_recognition_service.cc b/chrome/browser/speech/speech_recognition_service.cc
index 2cc2d75..d276ff6 100644
--- a/chrome/browser/speech/speech_recognition_service.cc
+++ b/chrome/browser/speech/speech_recognition_service.cc
@@ -33,8 +33,6 @@
   // is enabled. Otherwise, use the utility sandbox type.
 #if BUILDFLAG(ENABLE_SODA)
           .WithSandboxType(service_manager::SandboxType::kSpeechRecognition)
-#else
-          .WithSandboxType(service_manager::SandboxType::kUtility)
 #endif  // BUILDFLAG(ENABLE_SODA)
           .Pass());
 
diff --git a/chrome/browser/sync/test/integration/sync_errors_test.cc b/chrome/browser/sync/test/integration/sync_errors_test.cc
index cb2596d9..f2459b0 100644
--- a/chrome/browser/sync/test/integration/sync_errors_test.cc
+++ b/chrome/browser/sync/test/integration/sync_errors_test.cc
@@ -238,6 +238,32 @@
   ASSERT_NE(old_cache_guid, status.sync_id);
 }
 
+IN_PROC_BROWSER_TEST_F(SyncErrorTest, EncryptionObsoleteErrorTest) {
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+  const BookmarkNode* node1 = AddFolder(0, 0, "title1");
+  SetTitle(0, node1, "new_title1");
+  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
+
+  GetFakeServer()->TriggerActionableError(
+      sync_pb::SyncEnums::ENCRYPTION_OBSOLETE, "Not My Fault", "www.google.com",
+      sync_pb::SyncEnums::UNKNOWN_ACTION);
+
+  // Now make one more change so we will do another sync.
+  const BookmarkNode* node2 = AddFolder(0, 0, "title2");
+  SetTitle(0, node2, "new_title2");
+  EXPECT_TRUE(SyncDisabledChecker(GetSyncService(0)).Wait());
+
+  // Note: If SyncStandaloneTransport is enabled, then on receiving the error,
+  // the SyncService will immediately start up again in transport mode, which
+  // resets the status. So query the status that the checker recorded at the
+  // time Sync was off.
+  syncer::SyncStatus status;
+  GetSyncService(0)->QueryDetailedSyncStatusForDebugging(&status);
+  EXPECT_EQ(status.sync_protocol_error.error_type, syncer::ENCRYPTION_OBSOLETE);
+  EXPECT_EQ(status.sync_protocol_error.action, syncer::DISABLE_SYNC_ON_CLIENT);
+}
+
 IN_PROC_BROWSER_TEST_F(SyncErrorTest, DisableDatatypeWhileRunning) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   syncer::ModelTypeSet synced_datatypes =
diff --git a/chrome/browser/ui/ash/assistant/assistant_client_impl.cc b/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
index 7b5eb2cf..760bd86 100644
--- a/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
@@ -187,7 +187,6 @@
   content::ServiceProcessHost::Launch(
       std::move(receiver),
       content::ServiceProcessHost::Options()
-          .WithSandboxType(service_manager::SandboxType::kUtility)
           .WithDisplayName("Assistant Audio Decoder Service")
           .Pass());
 }
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc
index 1d1b3b3..9ac592e 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.cc
@@ -28,6 +28,9 @@
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/services/app_service/public/cpp/instance.h"
 #include "chrome/services/app_service/public/mojom/types.mojom-shared.h"
 #include "chrome/services/app_service/public/mojom/types.mojom.h"
@@ -70,6 +73,11 @@
         std::make_unique<AppServiceAppWindowCrostiniTracker>(this);
 
   profile_list_.push_back(owner->profile());
+
+  for (auto* browser : *BrowserList::GetInstance()) {
+    if (browser && browser->window() && browser->window()->GetNativeWindow())
+      observed_windows_.Add(browser->window()->GetNativeWindow());
+  }
 }
 
 AppServiceAppWindowLauncherController::
@@ -126,11 +134,12 @@
 void AppServiceAppWindowLauncherController::AdditionalUserAddedToSession(
     Profile* profile) {
   // Each users InstanceRegister needs to be observed.
-  apps::AppServiceProxy* proxy =
-      apps::AppServiceProxyFactory::GetForProfile(profile);
-  DCHECK(proxy);
-  proxy->InstanceRegistry().AddObserver(this);
+  proxy_ = apps::AppServiceProxyFactory::GetForProfile(profile);
+  DCHECK(proxy_);
+  proxy_->InstanceRegistry().AddObserver(this);
   profile_list_.push_back(profile);
+
+  app_service_instance_helper_->AdditionalUserAddedToSession(profile);
 }
 
 void AppServiceAppWindowLauncherController::OnWindowInitialized(
@@ -161,9 +170,7 @@
   if (shelf_id.IsNull())
     return;
 
-  DCHECK(proxy_);
-  if (proxy_->AppRegistryCache().GetAppType(shelf_id.app_id) !=
-      apps::mojom::AppType::kBuiltIn)
+  if (GetAppType(shelf_id.app_id) != apps::mojom::AppType::kBuiltIn)
     return;
 
   app_service_instance_helper_->OnInstances(shelf_id.app_id, window,
@@ -183,7 +190,7 @@
   if (arc_tracker_)
     arc_tracker_->OnWindowVisibilityChanged(window);
 
-  ash::ShelfID shelf_id = GetShelfId(window, false /*search_profile_list*/);
+  ash::ShelfID shelf_id = GetShelfId(window);
   if (shelf_id.IsNull())
     return;
 
@@ -202,8 +209,11 @@
   app_service_instance_helper_->OnInstances(GetAppId(shelf_id.app_id), window,
                                             shelf_id.launch_id, state);
 
-  if (!visible || shelf_id.app_id == extension_misc::kChromeAppId)
+  // Only register the visible non-browser |window| for the active user.
+  if (!visible || shelf_id.app_id == extension_misc::kChromeAppId ||
+      !proxy_->InstanceRegistry().Exists(window)) {
     return;
+  }
 
   RegisterWindow(window, shelf_id);
 
@@ -222,29 +232,27 @@
   // window could be teleported from the inactive user, and isn't saved in the
   // proxy of the active user's profile, but it should still be removed from
   // the controller, and the shelf, so search all the proxies.
-  ash::ShelfID shelf_id = GetShelfId(window, true /*search_profile_list*/);
-  if (shelf_id.IsNull()) {
+  std::string app_id = GetShelfId(window).app_id;
+  if (app_id.empty()) {
     // For Crostini apps, it could be run from the command line, and not saved
     // in AppService, so GetShelfId could return null when the window is
     // destroyed, but it should still be deleted from instance and remove the
     // app window from the shelf. So if we can get the window from
     // InstanceRegistry, we should still destroy it from InstanceRegistry and
     // remove the app window from the shelf
-    shelf_id = proxy_->InstanceRegistry().GetShelfId(window);
-    if (shelf_id.IsNull())
+    app_id = app_service_instance_helper_->GetAppId(window);
+    if (app_id.empty())
       return;
   }
 
-  if (app_service_instance_helper_->IsOpenedInBrowser(shelf_id.app_id,
-                                                      window) ||
-      shelf_id.app_id == extension_misc::kChromeAppId) {
+  if (app_service_instance_helper_->IsOpenedInBrowser(app_id, window) ||
+      app_id == extension_misc::kChromeAppId) {
     return;
   }
 
   // Delete the instance from InstanceRegistry.
-  app_service_instance_helper_->OnInstances(GetAppId(shelf_id.app_id), window,
-                                            std::string(),
-                                            apps::InstanceState::kDestroyed);
+  app_service_instance_helper_->OnInstances(
+      GetAppId(app_id), window, std::string(), apps::InstanceState::kDestroyed);
 
   auto app_window_it = aura_window_to_app_window_.find(window);
   if (app_window_it == aura_window_to_app_window_.end())
@@ -304,8 +312,7 @@
       (update.State() & apps::InstanceState::kDestroyed) ==
           apps::InstanceState::kUnknown) {
     std::string app_id = update.AppId();
-    if (proxy_->AppRegistryCache().GetAppType(app_id) ==
-            apps::mojom::AppType::kCrostini ||
+    if (GetAppType(app_id) == apps::mojom::AppType::kCrostini ||
         crostini::IsUnmatchedCrostiniShelfAppId(app_id)) {
       window->SetProperty(aura::client::kAppType,
                           static_cast<int>(ash::AppType::CROSTINI_APP));
@@ -418,8 +425,7 @@
   if (!window || !observed_windows_.IsObserving(window))
     return;
 
-  const ash::ShelfID shelf_id =
-      GetShelfId(window, false /*search_profile_list*/);
+  const ash::ShelfID shelf_id = GetShelfId(window);
   if (shelf_id.IsNull())
     return;
 
@@ -541,8 +547,7 @@
 }
 
 ash::ShelfID AppServiceAppWindowLauncherController::GetShelfId(
-    aura::Window* window,
-    bool search_profile_list) const {
+    aura::Window* window) const {
   if (crostini_tracker_) {
     std::string shelf_app_id;
     shelf_app_id = crostini_tracker_->GetShelfAppId(window);
@@ -562,47 +567,32 @@
 
   // If the window exists in InstanceRegistry, get the shelf id from
   // InstanceRegistry.
-  if (!search_profile_list) {
-    // Search from the proxy of the active user's profile, and verify whether
-    // the app exists in the proxy.
-    shelf_id = proxy_->InstanceRegistry().GetShelfId(window);
-    if (shelf_id.IsNull()) {
-      shelf_id =
-          ash::ShelfID::Deserialize(window->GetProperty(ash::kShelfIDKey));
-    }
-    if (!shelf_id.IsNull()) {
-      if (proxy_->AppRegistryCache().GetAppType(shelf_id.app_id) ==
-              apps::mojom::AppType::kUnknown &&
-          shelf_id.app_id != extension_misc::kChromeAppId) {
-        return ash::ShelfID();
-      }
-      return shelf_id;
-    }
-  } else {
-    for (auto* profile : profile_list_) {
-      auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
-      shelf_id = proxy->InstanceRegistry().GetShelfId(window);
-      if (!shelf_id.IsNull())
-        break;
-    }
-    if (shelf_id.IsNull()) {
-      shelf_id =
-          ash::ShelfID::Deserialize(window->GetProperty(ash::kShelfIDKey));
-    }
-    if (!shelf_id.IsNull()) {
-      for (auto* profile : profile_list_) {
-        auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
-        if (proxy->AppRegistryCache().GetAppType(shelf_id.app_id) !=
-                apps::mojom::AppType::kUnknown ||
-            shelf_id.app_id == extension_misc::kChromeAppId) {
-          return shelf_id;
-        }
-      }
-      return ash::ShelfID();
+  for (auto* profile : profile_list_) {
+    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
+    shelf_id = proxy->InstanceRegistry().GetShelfId(window);
+    if (!shelf_id.IsNull())
+      break;
+  }
+  if (shelf_id.IsNull()) {
+    shelf_id = ash::ShelfID::Deserialize(window->GetProperty(ash::kShelfIDKey));
+  }
+  if (!shelf_id.IsNull() &&
+      GetAppType(shelf_id.app_id) != apps::mojom::AppType::kUnknown) {
+    return shelf_id;
+  }
+  return ash::ShelfID();
+}
+
+apps::mojom::AppType AppServiceAppWindowLauncherController::GetAppType(
+    const std::string& app_id) const {
+  for (auto* profile : profile_list_) {
+    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
+    auto app_type = proxy->AppRegistryCache().GetAppType(app_id);
+    if (app_type != apps::mojom::AppType::kUnknown) {
+      return app_type;
     }
   }
-
-  return shelf_id;
+  return apps::mojom::AppType::kUnknown;
 }
 
 void AppServiceAppWindowLauncherController::UserHasAppOnActiveDesktop(
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.h b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.h
index 0525d624..c721357 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.h
@@ -123,15 +123,13 @@
   // AppWindowLauncherController:
   void OnItemDelegateDiscarded(ash::ShelfItemDelegate* delegate) override;
 
-  // Returns the shelf id for |window|. The window could be teleported from the
-  // inactive user to the active user. When |search_profile_list| is true, we
-  // should check the all proxies for all profiles, otherwise, only check the
-  // current active user profile's proxy.
-  //
-  // When |window|'s visibility or activate status is changed,
-  // |search_profile_list| is false to check the active user profile only. When
-  // |window| is destroyed, |search_profile_list| is true to check all proxies.
-  ash::ShelfID GetShelfId(aura::Window* window, bool search_profile_list) const;
+  // Returns the shelf id for |window|. |window| could be teleported from the
+  // inactive user to the active user, or during the user switch phase, |window|
+  // could belong to one of the profile.
+  ash::ShelfID GetShelfId(aura::Window* window) const;
+
+  // Returns the app type for the given |app_id|.
+  apps::mojom::AppType GetAppType(const std::string& app_id) const;
 
   // Register |window| if the owner of the given |window| has a window
   // teleported of the |window|'s application type to the current desktop.
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.cc b/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.cc
index 278bcda7..feda4c8 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.cc
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/stl_util.h"
 #include "base/time/time.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
@@ -47,6 +48,11 @@
       ProfileManager::GetActiveUserProfile());
 }
 
+void AppServiceInstanceRegistryHelper::AdditionalUserAddedToSession(
+    Profile* profile) {
+  proxy_ = apps::AppServiceProxyFactory::GetForProfile(profile);
+}
+
 void AppServiceInstanceRegistryHelper::OnActiveTabChanged(
     content::WebContents* old_contents,
     content::WebContents* new_contents) {
@@ -61,14 +67,14 @@
     // method is changed from windows to tabs, the app_id could be changed based
     // on the URL, e.g. google photos, which might cause instance app_id
     // inconsistent DCHECK error.
-    std::string app_id = proxy_->InstanceRegistry().GetShelfId(window).app_id;
+    std::string app_id = GetAppId(window);
     if (app_id.empty())
       app_id = launcher_controller_helper_->GetAppID(old_contents);
 
     // If app_id is empty, we should not set it as inactive because this is
     // Chrome's tab.
     if (!app_id.empty()) {
-      apps::InstanceState state = proxy_->InstanceRegistry().GetState(window);
+      apps::InstanceState state = GetState(window);
       // If the app has been inactive, we don't need to update the instance.
       if ((state & apps::InstanceState::kActive) !=
           apps::InstanceState::kUnknown) {
@@ -87,7 +93,7 @@
     // method is changed from windows to tabs, the app_id could be changed based
     // on the URL, e.g. google photos, which might cause instance app_id
     // inconsistent DCHECK error.
-    std::string app_id = proxy_->InstanceRegistry().GetShelfId(window).app_id;
+    std::string app_id = GetAppId(window);
     if (app_id.empty())
       app_id = GetAppId(new_contents);
 
@@ -121,12 +127,8 @@
   // could generate a temp instance for this window with the Chrome application
   // app_id. For this case, this temp instance can be deleted, otherwise, DCHECK
   // error for inconsistent app_id.
-  std::string old_app_id = app_id;
-  proxy_->InstanceRegistry().ForOneInstance(
-      window, [&old_app_id](const apps::InstanceUpdate& update) {
-        old_app_id = update.AppId();
-      });
-  if (app_id != old_app_id) {
+  const std::string old_app_id = GetAppId(window);
+  if (!old_app_id.empty() && app_id != old_app_id) {
     OnInstances(old_app_id, window, std::string(),
                 apps::InstanceState::kDestroyed);
   }
@@ -146,17 +148,16 @@
 
   // When the tab is closed, if the window does not exists in the AppService
   // InstanceRegistry, we don't need to update the status.
-  if (!proxy_->InstanceRegistry().Exists(window))
+  const std::string app_id = GetAppId(window);
+  if (app_id.empty())
     return;
 
-  std::string app_id = proxy_->InstanceRegistry().GetShelfId(window).app_id;
   RemoveTabWindow(app_id, window);
   OnInstances(app_id, window, std::string(), apps::InstanceState::kDestroyed);
 }
 
 void AppServiceInstanceRegistryHelper::OnBrowserRemoved() {
-  std::set<aura::Window*> windows =
-      proxy_->InstanceRegistry().GetWindows(extension_misc::kChromeAppId);
+  auto windows = GetWindows(extension_misc::kChromeAppId);
   for (auto* window : windows) {
     if (!chrome::FindBrowserWithWindow(window)) {
       // The browser is removed if the window can't be found, so update the
@@ -187,7 +188,7 @@
   // current active user, so search all proxies. If the instance is found from a
   // proxy, still save to that proxy, otherwise, save to the current active user
   // profile's proxy.
-  auto* proxy = proxy_;
+  apps::AppServiceProxy* proxy = proxy_;
   for (auto* profile : controller_->GetProfileList()) {
     auto* proxy_for_profile =
         apps::AppServiceProxyFactory::GetForProfile(profile);
@@ -208,8 +209,7 @@
     // window to compare with the parameter |window|, because we save the tab
     // window in AppService InstanceRegistry for Web apps, and we should set the
     // state for the tab window to keep one instance for the Web app.
-    std::set<aura::Window*> windows =
-        proxy_->InstanceRegistry().GetWindows(shelf_id.app_id);
+    auto windows = GetWindows(shelf_id.app_id);
     for (auto* it : windows) {
       if (it->GetToplevelWindow() != window)
         continue;
@@ -226,10 +226,9 @@
   // For Chrome browser app windows, sets the state for each tab window instance
   // in this browser.
   for (auto* it : browser_window_to_tab_window_[window]) {
-    if (!proxy_->InstanceRegistry().Exists(it))
+    const std::string app_id = GetAppId(it);
+    if (app_id.empty())
       continue;
-
-    std::string app_id = proxy_->InstanceRegistry().GetShelfId(it).app_id;
     apps::InstanceState state = CalculateVisibilityState(it, visible);
     OnInstances(app_id, it, std::string(), state);
   }
@@ -247,8 +246,7 @@
     // window to compare with |window|, because we save the tab
     // window in AppService InstanceRegistry for Web apps, and we should set the
     // state for the tab window to keep one instance for the Web app.
-    std::set<aura::Window*> windows =
-        proxy_->InstanceRegistry().GetWindows(shelf_id.app_id);
+    auto windows = GetWindows(shelf_id.app_id);
     for (auto* it : windows) {
       if (it->GetToplevelWindow() != window)
         continue;
@@ -278,8 +276,7 @@
     // Get the app_id from the existed instance first. The app_id for PWAs could
     // be changed based on the URL, e.g. google photos, which might cause
     // instance app_id inconsistent DCHECK error.
-    std::string app_id =
-        proxy_->InstanceRegistry().GetShelfId(contents_window).app_id;
+    const std::string app_id = GetAppId(contents_window);
     OnInstances(app_id.empty() ? GetAppId(contents) : app_id, contents_window,
                 std::string(), state);
     return;
@@ -288,9 +285,9 @@
   // For Chrome browser app windows, sets the state for each tab window instance
   // in this browser.
   for (auto* it : browser_window_to_tab_window_[window]) {
-    if (!proxy_->InstanceRegistry().Exists(it))
+    const std::string app_id = GetAppId(it);
+    if (app_id.empty())
       continue;
-    std::string app_id = proxy_->InstanceRegistry().GetShelfId(it).app_id;
     apps::InstanceState state = CalculateActivatedState(it, active);
     OnInstances(app_id, it, std::string(), state);
   }
@@ -302,7 +299,7 @@
 apps::InstanceState AppServiceInstanceRegistryHelper::CalculateVisibilityState(
     aura::Window* window,
     bool visible) const {
-  apps::InstanceState state = proxy_->InstanceRegistry().GetState(window);
+  apps::InstanceState state = GetState(window);
   state = static_cast<apps::InstanceState>(
       state | apps::InstanceState::kStarted | apps::InstanceState::kRunning);
   state = (visible) ? static_cast<apps::InstanceState>(
@@ -322,7 +319,7 @@
         apps::InstanceState::kActive | apps::InstanceState::kVisible);
   }
 
-  apps::InstanceState state = proxy_->InstanceRegistry().GetState(window);
+  apps::InstanceState state = GetState(window);
   state = static_cast<apps::InstanceState>(
       state | apps::InstanceState::kStarted | apps::InstanceState::kRunning);
   state =
@@ -338,10 +335,17 @@
   if (app_id == crostini::kCrostiniTerminalSystemAppId)
     return true;
 
-  apps::mojom::AppType app_type = proxy_->AppRegistryCache().GetAppType(app_id);
-  if (app_type != apps::mojom::AppType::kExtension &&
-      app_type != apps::mojom::AppType::kWeb) {
-    return false;
+  for (auto* profile : controller_->GetProfileList()) {
+    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
+    apps::mojom::AppType app_type =
+        proxy->AppRegistryCache().GetAppType(app_id);
+    if (app_type == apps::mojom::AppType::kUnknown)
+      continue;
+
+    if (app_type != apps::mojom::AppType::kExtension &&
+        app_type != apps::mojom::AppType::kWeb) {
+      return false;
+    }
   }
 
   // For Extension apps, and Web apps, AppServiceAppWindowLauncherController
@@ -371,6 +375,17 @@
 }
 
 std::string AppServiceInstanceRegistryHelper::GetAppId(
+    aura::Window* window) const {
+  for (auto* profile : controller_->GetProfileList()) {
+    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
+    std::string app_id = proxy->InstanceRegistry().GetShelfId(window).app_id;
+    if (!app_id.empty())
+      return app_id;
+  }
+  return std::string();
+}
+
+std::string AppServiceInstanceRegistryHelper::GetAppId(
     content::WebContents* contents) const {
   std::string app_id = launcher_controller_helper_->GetAppID(contents);
   if (!app_id.empty())
@@ -390,6 +405,28 @@
   return window;
 }
 
+std::set<aura::Window*> AppServiceInstanceRegistryHelper::GetWindows(
+    const std::string& app_id) {
+  std::set<aura::Window*> windows;
+  for (auto* profile : controller_->GetProfileList()) {
+    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
+    auto w = proxy->InstanceRegistry().GetWindows(app_id);
+    windows = base::STLSetUnion<std::set<aura::Window*>>(windows, w);
+  }
+  return windows;
+}
+
+apps::InstanceState AppServiceInstanceRegistryHelper::GetState(
+    aura::Window* window) const {
+  for (auto* profile : controller_->GetProfileList()) {
+    auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
+    auto state = proxy->InstanceRegistry().GetState(window);
+    if (state != apps::InstanceState::kUnknown)
+      return state;
+  }
+  return apps::InstanceState::kUnknown;
+}
+
 void AppServiceInstanceRegistryHelper::AddTabWindow(const std::string& app_id,
                                                     aura::Window* window) {
   if (app_id == extension_misc::kChromeAppId)
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h b/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h
index 64e63627..020faab 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h
@@ -5,7 +5,9 @@
 #ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_INSTANCE_REGISTRY_HELPER_H_
 #define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_INSTANCE_REGISTRY_HELPER_H_
 
+#include <map>
 #include <memory>
+#include <set>
 
 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
 #include "chrome/services/app_service/public/cpp/instance.h"
@@ -32,6 +34,7 @@
   ~AppServiceInstanceRegistryHelper();
 
   void ActiveUserChanged();
+  void AdditionalUserAddedToSession(Profile* profile);
 
   // Notifies the AppService InstanceRegistry that active tabs are changed.
   void OnActiveTabChanged(content::WebContents* old_contents,
@@ -82,6 +85,10 @@
   // Return true if the app is opend in a browser.
   bool IsOpenedInBrowser(const std::string& app_id, aura::Window* window) const;
 
+  // Returns an app id for |window| in InstanceRegistry. If there is no |window|
+  // in InstanceRegistry, returns an empty string.
+  std::string GetAppId(aura::Window* window) const;
+
  private:
   // Returns an app id to represent |contents| in InstanceRegistry. If there is
   // no app in |contents|, returns the app id of the Chrome component
@@ -93,6 +100,13 @@
   // |contents|, returns the toplevel window.
   aura::Window* GetWindow(content::WebContents* contents);
 
+  // Returns windows in InstanceRegistry for the given |app_id|.
+  std::set<aura::Window*> GetWindows(const std::string& app_id);
+
+  // Returns the state in InstanceRegistry for the given |app_id|. If there is
+  // no |window| in InstanceRegistry, returns apps::InstanceState::kUnknown.
+  apps::InstanceState GetState(aura::Window* window) const;
+
   // Adds the tab's |window| to |browser_window_to_tab_window_|.
   void AddTabWindow(const std::string& app_id, aura::Window* window);
   // Removes the tab's |window| from |browser_window_to_tab_window_|.
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index fbcc5d9..ff471ed 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -886,6 +886,7 @@
     const std::string app_id =
         ArcAppListPrefs::GetAppId(app_info.package_name, app_info.activity);
     EXPECT_TRUE(prefs->GetApp(app_id));
+    app_service_test().FlushMojoCalls();
     return app_id;
   }
 
@@ -3304,12 +3305,8 @@
 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
        V2AppHandlingTwoUsers) {
   InitLauncherController();
-  // Create a profile for our second user (will be destroyed by the framework).
-  TestingProfile* profile2 = CreateMultiUserProfile("user2");
   const AccountId account_id(
       multi_user_util::GetAccountIdFromProfile(profile()));
-  const AccountId account_id2(
-      multi_user_util::GetAccountIdFromProfile(profile2));
   // Check that there is a browser.
   EXPECT_EQ(1, model_->item_count());
 
@@ -3318,6 +3315,11 @@
   V2App v2_app(profile(), extension1_.get());
   EXPECT_EQ(2, model_->item_count());
 
+  // Create a profile for our second user (will be destroyed by the framework).
+  TestingProfile* profile2 = CreateMultiUserProfile("user2");
+  const AccountId account_id2(
+      multi_user_util::GetAccountIdFromProfile(profile2));
+
   // After switching users the item should go away.
   SwitchActiveUser(account_id2);
   EXPECT_EQ(1, model_->item_count());
@@ -3523,10 +3525,15 @@
   InitLauncherController();
 
   TestingProfile* profile2 = CreateMultiUserProfile("user-2");
-  const AccountId account_id(
-      multi_user_util::GetAccountIdFromProfile(profile()));
   const AccountId account_id2(
       multi_user_util::GetAccountIdFromProfile(profile2));
+  // If switch to account_id2 is not run, the following switch to account_id
+  // is invalid, because the user account is not changed, so switch to
+  // account_id2 first.
+  SwitchActiveUser(account_id2);
+
+  const AccountId account_id(
+      multi_user_util::GetAccountIdFromProfile(profile()));
   SwitchActiveUser(account_id);
   EXPECT_EQ(1, model_->item_count());
 
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.h b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
index aa930bc..c0c3dab9 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
@@ -21,7 +21,6 @@
 #include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
 #include "chrome/browser/ui/blocked_content/url_list_manager.h"
 #include "chrome/common/custom_handlers/protocol_handler.h"
-#include "chrome/grit/generated_resources.h"
 #include "components/content_settings/browser/tab_specific_content_settings.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 5fc0332..f37bf0c 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -159,8 +159,11 @@
   if (frame()->ShouldDrawFrameHeader())
     frame_header_ = CreateFrameHeader();
 
-  if (browser_view()->IsBrowserTypeWebApp())
-    SetUpForWebApp();
+  if (browser_view()->IsBrowserTypeWebApp() && !browser->is_type_app_popup()) {
+    // Add the container for extra web app buttons (e.g app menu button).
+    set_web_app_frame_toolbar(AddChildView(
+        std::make_unique<WebAppFrameToolbarView>(frame(), browser_view())));
+  }
 
   browser_view()->immersive_mode_controller()->AddObserver(this);
 }
@@ -697,12 +700,6 @@
   return header;
 }
 
-void BrowserNonClientFrameViewAsh::SetUpForWebApp() {
-  // Add the container for extra web app buttons (e.g app menu button).
-  set_web_app_frame_toolbar(AddChildView(
-      std::make_unique<WebAppFrameToolbarView>(frame(), browser_view())));
-}
-
 void BrowserNonClientFrameViewAsh::UpdateTopViewInset() {
   // In immersive fullscreen mode, the top view inset property should be 0.
   const bool immersive =
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
index b9adc2fc..8937981 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -187,9 +187,6 @@
   // Creates the frame header for the browser window.
   std::unique_ptr<ash::FrameHeader> CreateFrameHeader();
 
-  // Creates views and does other setup for a web app.
-  void SetUpForWebApp();
-
   // Triggers the web-app origin and icon animations, assumes the web-app UI
   // elements exist.
   void StartWebAppAnimation();
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index f8d29fb0..7960fee4 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -39,6 +39,8 @@
 #include "chrome/browser/ui/browser_command_controller.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller_test.h"
@@ -87,7 +89,9 @@
 #include "ui/aura/test/env_test_helper.h"
 #include "ui/base/class_property.h"
 #include "ui/base/hit_test.h"
+#include "ui/base/page_transition_types.h"
 #include "ui/base/pointer/touch_ui_controller.h"
+#include "ui/base/window_open_disposition.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
@@ -1236,6 +1240,26 @@
   EXPECT_FALSE(GetPaintingAsActive());
 }
 
+IN_PROC_BROWSER_TEST_P(WebAppNonClientFrameViewAshTest, PopupHasNoToolbar) {
+  SetUpWebApp();
+  {
+    NavigateParams navigate_params(app_browser_, GetAppURL(),
+                                   ui::PAGE_TRANSITION_LINK);
+    navigate_params.disposition = WindowOpenDisposition::NEW_POPUP;
+
+    content::TestNavigationObserver navigation_observer(GetAppURL());
+    navigation_observer.StartWatchingNewWebContents();
+    Navigate(&navigate_params);
+    navigation_observer.WaitForNavigationFinished();
+  }
+
+  Browser* popup_browser = BrowserList::GetInstance()->GetLastActive();
+  BrowserView* browser_view =
+      BrowserView::GetBrowserViewForBrowser(popup_browser);
+  BrowserNonClientFrameViewAsh* frame_view = GetFrameViewAsh(browser_view);
+  EXPECT_FALSE(frame_view->web_app_frame_toolbar_for_testing());
+}
+
 namespace {
 
 class BrowserNonClientFrameViewAshBackButtonTest
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
index 718ddf0..c4eccfe 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
+++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/content_setting_bubble_contents.h"
 #include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h"
+#include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/theme_provider.h"
 #include "ui/events/event_utils.h"
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index ba6c220f..51c3fa62 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -80,7 +80,7 @@
   remove_suggestion_focus_ring_ =
       views::FocusRing::Install(remove_suggestion_button_);
   remove_suggestion_focus_ring_->SetHasFocusPredicate([&](View* view) {
-    return view->GetVisible() && IsSelected() &&
+    return view->GetVisible() && IsMatchSelected() &&
            popup_contents_view_->IsButtonSelected();
   });
 
@@ -219,7 +219,7 @@
 
 void OmniboxResultView::OnSelectionStateChanged() {
   UpdateRemoveSuggestionVisibility();
-  if (IsSelected()) {
+  if (IsMatchSelected()) {
     // Immediately before notifying screen readers that the selected item has
     // changed, we want to update the name of the newly-selected item so that
     // any cached values get updated prior to the selection change.
@@ -243,8 +243,11 @@
   ApplyThemeAndRefreshIcons();
 }
 
-bool OmniboxResultView::IsSelected() const {
-  return popup_contents_view_->IsSelectedIndex(model_index_);
+bool OmniboxResultView::IsMatchSelected() const {
+  // The header button being focused means the match itself is NOT focused.
+  return popup_contents_view_->IsSelectedIndex(model_index_) &&
+         popup_contents_view_->model()->selected_line_state() !=
+             OmniboxPopupModel::HEADER_BUTTON_FOCUSED;
 }
 
 views::Button* OmniboxResultView::GetSecondaryButton() {
@@ -272,9 +275,9 @@
     int* label_prefix_length) {
   int additional_message_id = 0;
   views::Button* secondary_button = GetSecondaryButton();
-  bool button_focused =
-      IsSelected() && popup_contents_view_->model()->selected_line_state() ==
-                          OmniboxPopupModel::BUTTON_FOCUSED;
+  bool button_focused = IsMatchSelected() &&
+                        popup_contents_view_->model()->selected_line_state() ==
+                            OmniboxPopupModel::BUTTON_FOCUSED;
 
   // If there's a button focused, we don't want the "n of m" message announced.
   if (button_focused)
@@ -303,7 +306,7 @@
 }
 
 OmniboxPartState OmniboxResultView::GetThemeState() const {
-  if (IsSelected())
+  if (IsMatchSelected())
     return OmniboxPartState::SELECTED;
 
   // If we don't highlight the whole row when the user has the mouse over the
@@ -445,7 +448,7 @@
     // When the drag enters or remains within the bounds of this view, either
     // set the state to be selected or hovered, depending on the mouse button.
     if (event.IsOnlyLeftMouseButton()) {
-      if (!IsSelected())
+      if (!IsMatchSelected())
         popup_contents_view_->SetSelectedLine(model_index_);
       if (suggestion_tab_switch_button_) {
         gfx::Point point_in_child_coords(event.location());
@@ -509,7 +512,7 @@
                              popup_contents_view_->model()->result().size());
 
   node_data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected,
-                              IsSelected());
+                              IsMatchSelected());
   if (IsMouseHovered())
     node_data->AddState(ax::mojom::State::kHovered);
 }
@@ -594,7 +597,7 @@
                         !match_.ShouldShowTabMatchButton() &&
                         base::FeatureList::IsEnabled(
                             omnibox::kOmniboxSuggestionTransparencyOptions) &&
-                        (IsSelected() || IsMouseHovered());
+                        (IsMatchSelected() || IsMouseHovered());
 
   remove_suggestion_button_->SetVisible(new_visibility);
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.h b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
index 81ef18e..b6f81b7 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
@@ -71,8 +71,9 @@
   // Invoked when this result view has been selected or unselected.
   void OnSelectionStateChanged();
 
-  // Whether |this| matches the model's selected index.
-  bool IsSelected() const;
+  // Whether this result view should be considered 'selected'. This returns
+  // false if this line's header is selected (instead of the match itself).
+  bool IsMatchSelected() const;
 
   // Returns the visible (and keyboard-focusable) secondary button, or nullptr
   // if none exists for this suggestion.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc b/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc
index df761d0..9f62d8e7 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc
@@ -111,7 +111,8 @@
 
 bool OmniboxTabSwitchButton::IsSelected() const {
   // Is this result selected and is button selected?
-  return result_view_->IsSelected() && popup_contents_view_->IsButtonSelected();
+  return result_view_->IsMatchSelected() &&
+         popup_contents_view_->IsButtonSelected();
 }
 
 int OmniboxTabSwitchButton::CalculateGoalWidth(int parent_width,
diff --git a/chrome/browser/ui/views/permission_bubble/permission_bubble_interactive_uitest.cc b/chrome/browser/ui/views/permission_bubble/permission_bubble_interactive_uitest.cc
index 7701cec1..946e7b9cd 100644
--- a/chrome/browser/ui/views/permission_bubble/permission_bubble_interactive_uitest.cc
+++ b/chrome/browser/ui/views/permission_bubble/permission_bubble_interactive_uitest.cc
@@ -22,6 +22,11 @@
  public:
   PermissionBubbleInteractiveUITest() = default;
 
+  PermissionBubbleInteractiveUITest(const PermissionBubbleInteractiveUITest&) =
+      delete;
+  PermissionBubbleInteractiveUITest& operator=(
+      const PermissionBubbleInteractiveUITest&) = delete;
+
   void EnsureWindowActive(ui::BaseWindow* window, const char* message) {
     EnsureWindowActive(
         views::Widget::GetWidgetForNativeWindow(window->GetNativeWindow()),
@@ -37,7 +42,7 @@
   }
 
   // Send Ctrl/Cmd+keycode in the key window to the browser.
-  void SendAccelerator(ui::KeyboardCode keycode, bool shift, bool alt) {
+  void SendAcceleratorSync(ui::KeyboardCode keycode, bool shift, bool alt) {
 #if defined(OS_MACOSX)
     bool control = false;
     bool command = true;
@@ -45,8 +50,9 @@
     bool control = true;
     bool command = false;
 #endif
-    ui_controls::SendKeyPress(browser()->window()->GetNativeWindow(), keycode,
-                              control, shift, alt, command);
+
+    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), keycode, control,
+                                                shift, alt, command));
   }
 
   void SetUpOnMainThread() override {
@@ -66,16 +72,48 @@
     EnsureWindowActive(test_api_->GetPromptWindow(), "show permission bubble");
   }
 
+  void JumpToNextOpenTab() {
+#if defined(OS_MACOSX)
+    SendAcceleratorSync(ui::VKEY_RIGHT, false, true);
+#else
+    SendAcceleratorSync(ui::VKEY_TAB, false, false);
+#endif
+  }
+
+  void JumpToPreviousOpenTab() {
+#if defined(OS_MACOSX)
+    SendAcceleratorSync(ui::VKEY_LEFT, false, true);
+#else
+    SendAcceleratorSync(ui::VKEY_TAB, true, false);
+#endif
+  }
+
+  void TestSwitchingTabsWithCurlyBraces() {
+    // Also test switching tabs with curly braces. "VKEY_OEM_4" is
+    // LeftBracket/Brace on a US keyboard, which ui::MacKeyCodeForWindowsKeyCode
+    // will map to '{' when shift is passed. Also note there are only two tabs
+    // so it doesn't matter which direction is taken (it wraps).
+    chrome::FocusLocationBar(browser());
+    SendAcceleratorSync(ui::VKEY_OEM_4, true, false);
+    EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
+    EnsureWindowActive(test_api_->GetPromptWindow(),
+                       "switch to permission tab with curly brace");
+    EXPECT_TRUE(test_api_->GetPromptWindow());
+
+    SendAcceleratorSync(ui::VKEY_OEM_4, true, false);
+    EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
+    browser()->window()->Activate();
+    EnsureWindowActive(browser()->window(), "switch away with curly brace");
+    EXPECT_FALSE(test_api_->GetPromptWindow());
+  }
+
  protected:
   std::unique_ptr<test::PermissionRequestManagerTestApi> test_api_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PermissionBubbleInteractiveUITest);
 };
 
-#if defined(OS_WIN) || defined(OS_LINUX)
-// TODO(https://crbug.com/866878): Accelerators are broken when this bubble is
-// showing on non-Mac.
+#if defined(OS_CHROMEOS)
+// TODO(crbug.com/1072425): views::test::WidgetTest::GetAllWidgets() crashes
+// on Chrome OS, need to investigate\fix that.
 #define MAYBE_CmdWClosesWindow DISABLED_CmdWClosesWindow
 #else
 #define MAYBE_CmdWClosesWindow CmdWClosesWindow
@@ -87,27 +125,16 @@
                        MAYBE_CmdWClosesWindow) {
   EXPECT_TRUE(browser()->window()->IsVisible());
 
-  SendAccelerator(ui::VKEY_W, false, false);
+  SendAcceleratorSync(ui::VKEY_W, false, false);
 
-  // The actual window close happens via a posted task.
-  EXPECT_TRUE(browser()->window()->IsVisible());
-  ui_test_utils::WaitForBrowserToClose(browser());
-  // The window has been destroyed at this point, so there should be no widgets
-  // hanging around.
+  // The window has been destroyed so there should be no widgets hanging around.
   EXPECT_EQ(0u, views::test::WidgetTest::GetAllWidgets().size());
 }
 
-#if defined(OS_WIN) || defined(OS_LINUX)
-// TODO(https://crbug.com/866878): Accelerators are broken when this bubble is
-// showing on non-Mac.
-#define MAYBE_SwitchTabs DISABLED_SwitchTabs
-#else
-#define MAYBE_SwitchTabs SwitchTabs
-#endif
-
-// Add a tab, ensure we can switch away and back using Ctrl/Cmd+Alt+Left/Right
-// and curly braces.
-IN_PROC_BROWSER_TEST_F(PermissionBubbleInteractiveUITest, MAYBE_SwitchTabs) {
+// Add a tab, ensure we can switch away and back using Ctrl+Tab and
+// Ctrl+Shift+Tab at aura and using Cmd+Alt+Left/Right and curly braces at
+// MacOS.
+IN_PROC_BROWSER_TEST_F(PermissionBubbleInteractiveUITest, SwitchTabs) {
   EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
   EXPECT_TRUE(test_api_->GetPromptWindow());
 
@@ -115,12 +142,14 @@
   AddBlankTabAndShow(browser());
   EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
 
+#if defined(OS_MACOSX)
   // The bubble should hide and give focus back to the browser. However, the
   // test environment can't guarantee that macOS decides that the Browser window
   // is actually the "best" window to activate upon closing the current key
   // window. So activate it manually.
   browser()->window()->Activate();
   EnsureWindowActive(browser()->window(), "tab added");
+#endif
 
   // Prompt is hidden while its tab is not active.
   EXPECT_FALSE(test_api_->GetPromptWindow());
@@ -129,37 +158,27 @@
   // accelerator before sending it back unhandled to the browser via IPC. That's
   // all a bit much to handle in a test, so activate the location bar.
   chrome::FocusLocationBar(browser());
-  SendAccelerator(ui::VKEY_LEFT, false, true);
+
+  JumpToPreviousOpenTab();
   EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
 
-  // Note we don't need to makeKeyAndOrderFront: the permission window will take
-  // focus when it is shown again.
-  EnsureWindowActive(test_api_->GetPromptWindow(),
-                     "switched to permission tab with arrow");
+  // Note we don't need to makeKeyAndOrderFront for mac os: the permission
+  // window will take focus when it is shown again.
+  EnsureWindowActive(
+      test_api_->GetPromptWindow(),
+      "switched to permission tab with ctrl+shift+tab or arrow at mac os");
   EXPECT_TRUE(test_api_->GetPromptWindow());
 
   // Ensure we can switch away with the bubble active.
-  SendAccelerator(ui::VKEY_RIGHT, false, true);
+  JumpToNextOpenTab();
   EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
 
   browser()->window()->Activate();
-  EnsureWindowActive(browser()->window(), "switch away with arrow");
+  EnsureWindowActive(browser()->window(),
+                     "switch away with ctrl+tab or arrow at mac os");
   EXPECT_FALSE(test_api_->GetPromptWindow());
 
-  // Also test switching tabs with curly braces. "VKEY_OEM_4" is
-  // LeftBracket/Brace on a US keyboard, which ui::MacKeyCodeForWindowsKeyCode
-  // will map to '{' when shift is passed. Also note there are only two tabs so
-  // it doesn't matter which direction is taken (it wraps).
-  chrome::FocusLocationBar(browser());
-  SendAccelerator(ui::VKEY_OEM_4, true, false);
-  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
-  EnsureWindowActive(test_api_->GetPromptWindow(),
-                     "switch to permission tab with curly brace");
-  EXPECT_TRUE(test_api_->GetPromptWindow());
-
-  SendAccelerator(ui::VKEY_OEM_4, true, false);
-  EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
-  browser()->window()->Activate();
-  EnsureWindowActive(browser()->window(), "switch away with curly brace");
-  EXPECT_FALSE(test_api_->GetPromptWindow());
+#if defined(OS_MACOSX)
+  TestSwitchingTabsWithCurlyBraces();
+#endif
 }
diff --git a/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc b/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc
index 03c4b2e..4de15dc 100644
--- a/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc
+++ b/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc
@@ -285,7 +285,6 @@
   base::HistogramTester::CountsMap expected_counts = {
       {HistogramName("DevicesToShow"), 1},
       {HistogramName("DevicesToShow.ContextMenu"), 1},
-      {HistogramName("ContextMenuPhoneNumberParsingDelay"), 1},
   };
 
   EXPECT_THAT(GetTotalHistogramCounts(histograms),
diff --git a/chrome/browser/ui/webui/autofill_and_password_manager_internals/OWNERS b/chrome/browser/ui/webui/autofill_and_password_manager_internals/OWNERS
index c91bb70..f7b1a42 100644
--- a/chrome/browser/ui/webui/autofill_and_password_manager_internals/OWNERS
+++ b/chrome/browser/ui/webui/autofill_and_password_manager_internals/OWNERS
@@ -1,5 +1,5 @@
 battre@chromium.org
 dvadym@chromium.org
-koerber@chromium.org
+koerber@google.com
 
 # COMPONENT: UI>Browser>Passwords
diff --git a/chrome/browser/ui/webui/settings/safety_check_handler.cc b/chrome/browser/ui/webui/settings/safety_check_handler.cc
index ac4ca81c..b1831b3d 100644
--- a/chrome/browser/ui/webui/settings/safety_check_handler.cc
+++ b/chrome/browser/ui/webui/settings/safety_check_handler.cc
@@ -23,6 +23,8 @@
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/version_info/version_info.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
 #include "extensions/browser/extension_prefs_factory.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/extension_id.h"
@@ -125,6 +127,13 @@
     version_updater_.reset(VersionUpdater::Create(web_ui()->GetWebContents()));
   }
   DCHECK(version_updater_);
+  if (!update_helper_) {
+    update_helper_.reset(new safety_check::UpdateCheckHelper(
+        content::BrowserContext::GetDefaultStoragePartition(
+            Profile::FromWebUI(web_ui()))
+            ->GetURLLoaderFactoryForBrowserProcess()));
+  }
+  DCHECK(update_helper_);
   CheckUpdates();
 
   if (!leak_service_) {
@@ -155,12 +164,14 @@
 }
 
 SafetyCheckHandler::SafetyCheckHandler(
+    std::unique_ptr<safety_check::UpdateCheckHelper> update_helper,
     std::unique_ptr<VersionUpdater> version_updater,
     password_manager::BulkLeakCheckService* leak_service,
     extensions::PasswordsPrivateDelegate* passwords_delegate,
     extensions::ExtensionPrefs* extension_prefs,
     extensions::ExtensionServiceInterface* extension_service)
-    : version_updater_(std::move(version_updater)),
+    : update_helper_(std::move(update_helper)),
+      version_updater_(std::move(version_updater)),
       leak_service_(leak_service),
       passwords_delegate_(passwords_delegate),
       extension_prefs_(extension_prefs),
@@ -192,7 +203,7 @@
 void SafetyCheckHandler::CheckUpdates() {
   // Usage of base::Unretained(this) is safe, because we own `version_updater_`.
   version_updater_->CheckForUpdate(
-      base::Bind(&SafetyCheckHandler::OnUpdateCheckResult,
+      base::Bind(&SafetyCheckHandler::OnVersionUpdaterResult,
                  base::Unretained(this)),
       VersionUpdater::PromoteCallback());
 }
@@ -262,13 +273,8 @@
   }
 }
 
-void SafetyCheckHandler::OnUpdateCheckResult(VersionUpdater::Status status,
-                                             int progress,
-                                             bool rollback,
-                                             const std::string& version,
-                                             int64_t update_size,
-                                             const base::string16& message) {
-  update_status_ = ConvertToUpdateStatus(status);
+void SafetyCheckHandler::OnUpdateCheckResult(UpdateStatus status) {
+  update_status_ = status;
   if (update_status_ != UpdateStatus::kChecking) {
     base::UmaHistogramEnumeration("Settings.SafetyCheck.UpdatesResult",
                                   update_status_);
@@ -544,6 +550,11 @@
   }
 }
 
+void SafetyCheckHandler::DetermineIfOfflineOrError(bool connected) {
+  OnUpdateCheckResult(connected ? UpdateStatus::kFailed
+                                : UpdateStatus::kFailedOffline);
+}
+
 void SafetyCheckHandler::DetermineIfNoPasswordsOrSafe(
     const std::vector<extensions::api::passwords_private::PasswordUiEntry>&
         passwords) {
@@ -552,6 +563,21 @@
                          Compromised(0), Done(0), Total(0));
 }
 
+void SafetyCheckHandler::OnVersionUpdaterResult(VersionUpdater::Status status,
+                                                int progress,
+                                                bool rollback,
+                                                const std::string& version,
+                                                int64_t update_size,
+                                                const base::string16& message) {
+  if (status == VersionUpdater::FAILED) {
+    update_helper_->CheckConnectivity(
+        base::BindOnce(&SafetyCheckHandler::DetermineIfOfflineOrError,
+                       base::Unretained(this)));
+    return;
+  }
+  OnUpdateCheckResult(ConvertToUpdateStatus(status));
+}
+
 void SafetyCheckHandler::OnSafeBrowsingCheckResult(
     SafetyCheckHandler::SafeBrowsingStatus status) {
   safe_browsing_status_ = status;
diff --git a/chrome/browser/ui/webui/settings/safety_check_handler.h b/chrome/browser/ui/webui/settings/safety_check_handler.h
index 01242a2..0c73407 100644
--- a/chrome/browser/ui/webui/settings/safety_check_handler.h
+++ b/chrome/browser/ui/webui/settings/safety_check_handler.h
@@ -21,6 +21,7 @@
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 #include "components/password_manager/core/browser/bulk_leak_check_service.h"
 #include "components/safety_check/safety_check.h"
+#include "components/safety_check/update_check_helper.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 
@@ -105,11 +106,13 @@
                                        base::Time system_time);
 
  protected:
-  SafetyCheckHandler(std::unique_ptr<VersionUpdater> version_updater,
-                     password_manager::BulkLeakCheckService* leak_service,
-                     extensions::PasswordsPrivateDelegate* passwords_delegate,
-                     extensions::ExtensionPrefs* extension_prefs,
-                     extensions::ExtensionServiceInterface* extension_service);
+  SafetyCheckHandler(
+      std::unique_ptr<safety_check::UpdateCheckHelper> update_helper,
+      std::unique_ptr<VersionUpdater> version_updater,
+      password_manager::BulkLeakCheckService* leak_service,
+      extensions::PasswordsPrivateDelegate* passwords_delegate,
+      extensions::ExtensionPrefs* extension_prefs,
+      extensions::ExtensionServiceInterface* extension_service);
 
   void SetVersionUpdaterForTesting(
       std::unique_ptr<VersionUpdater> version_updater) {
@@ -147,12 +150,7 @@
   void CheckExtensions();
 
   // Callbacks that get triggered when each check completes.
-  void OnUpdateCheckResult(VersionUpdater::Status status,
-                           int progress,
-                           bool rollback,
-                           const std::string& version,
-                           int64_t update_size,
-                           const base::string16& message);
+  void OnUpdateCheckResult(UpdateStatus status);
   void OnPasswordsCheckResult(PasswordsStatus status,
                               Compromised compromised,
                               Done done,
@@ -176,6 +174,10 @@
                                         ReenabledUser reenabled_user,
                                         ReenabledAdmin reenabled_admin);
 
+  // A generic error state often includes the offline state. This method is used
+  // as a callback for |UpdateCheckHelper| to check connectivity.
+  void DetermineIfOfflineOrError(bool connected);
+
   // Since the password check API does not distinguish between the cases of
   // having no compromised passwords and not having any passwords at all, it is
   // necessary to use this method as a callback for
@@ -185,6 +187,15 @@
       const std::vector<extensions::api::passwords_private::PasswordUiEntry>&
           passwords);
 
+  // A callback passed to |VersionUpdater::CheckForUpdate| to receive the update
+  // state.
+  void OnVersionUpdaterResult(VersionUpdater::Status status,
+                              int progress,
+                              bool rollback,
+                              const std::string& version,
+                              int64_t update_size,
+                              const base::string16& message);
+
   // SafetyCheck::SafetyCheckHandlerInterface implementation.
   void OnSafeBrowsingCheckResult(SafeBrowsingStatus status) override;
 
@@ -221,6 +232,7 @@
   base::Time safety_check_completion_time_;
 
   std::unique_ptr<safety_check::SafetyCheck> safety_check_;
+  std::unique_ptr<safety_check::UpdateCheckHelper> update_helper_;
 
   std::unique_ptr<VersionUpdater> version_updater_;
   password_manager::BulkLeakCheckService* leak_service_ = nullptr;
diff --git a/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc b/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
index 89e964d..c6e18dd 100644
--- a/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
@@ -29,6 +29,7 @@
 #include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safety_check/test_update_check_helper.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/version_info/version_info.h"
 #include "content/public/test/test_web_ui.h"
@@ -61,12 +62,14 @@
   using SafetyCheckHandler::SetVersionUpdaterForTesting;
 
   TestingSafetyCheckHandler(
+      std::unique_ptr<safety_check::UpdateCheckHelper> update_helper,
       std::unique_ptr<VersionUpdater> version_updater,
       password_manager::BulkLeakCheckService* leak_service,
       extensions::PasswordsPrivateDelegate* passwords_delegate,
       extensions::ExtensionPrefs* extension_prefs,
       extensions::ExtensionServiceInterface* extension_service)
-      : SafetyCheckHandler(std::move(version_updater),
+      : SafetyCheckHandler(std::move(update_helper),
+                           std::move(version_updater),
                            leak_service,
                            passwords_delegate,
                            extension_prefs,
@@ -195,6 +198,7 @@
                            const std::string& expected);
 
  protected:
+  safety_check::TestUpdateCheckHelper* update_helper_ = nullptr;
   TestVersionUpdater* version_updater_ = nullptr;
   std::unique_ptr<password_manager::BulkLeakCheckService> test_leak_service_;
   TestPasswordsDelegate test_passwords_delegate_;
@@ -217,17 +221,19 @@
   // The unique pointer to a TestVersionUpdater gets moved to
   // SafetyCheckHandler, but a raw pointer is retained here to change its
   // state.
+  auto update_helper = std::make_unique<safety_check::TestUpdateCheckHelper>();
+  update_helper_ = update_helper.get();
   auto version_updater = std::make_unique<TestVersionUpdater>();
+  version_updater_ = version_updater.get();
   test_leak_service_ = std::make_unique<password_manager::BulkLeakCheckService>(
       nullptr, nullptr);
   test_passwords_delegate_.SetBulkLeakCheckService(test_leak_service_.get());
-  version_updater_ = version_updater.get();
   test_web_ui_.set_web_contents(web_contents());
   test_extension_prefs_ = extensions::ExtensionPrefs::Get(profile());
   safety_check_ = std::make_unique<TestingSafetyCheckHandler>(
-      std::move(version_updater), test_leak_service_.get(),
-      &test_passwords_delegate_, test_extension_prefs_,
-      &test_extension_service_);
+      std::move(update_helper), std::move(version_updater),
+      test_leak_service_.get(), &test_passwords_delegate_,
+      test_extension_prefs_, &test_extension_service_);
   test_web_ui_.ClearTrackedCalls();
   safety_check_->set_web_ui(&test_web_ui_);
   safety_check_->AllowJavascript();
@@ -425,7 +431,8 @@
       SafetyCheckHandler::UpdateStatus::kFailedOffline, 1);
 }
 
-TEST_F(SafetyCheckHandlerTest, CheckUpdates_Failed) {
+TEST_F(SafetyCheckHandlerTest, CheckUpdates_Failed_ConnectivityOnline) {
+  update_helper_->SetConnectivity(true);
   version_updater_->SetReturnedStatus(VersionUpdater::Status::FAILED);
   safety_check_->PerformSafetyCheck();
   const base::DictionaryValue* event =
@@ -443,6 +450,23 @@
                                       1);
 }
 
+TEST_F(SafetyCheckHandlerTest, CheckUpdates_Failed_ConnectivityOffline) {
+  update_helper_->SetConnectivity(false);
+  version_updater_->SetReturnedStatus(VersionUpdater::Status::FAILED);
+  safety_check_->PerformSafetyCheck();
+  const base::DictionaryValue* event =
+      GetSafetyCheckStatusChangedWithDataIfExists(
+          kUpdates,
+          static_cast<int>(SafetyCheckHandler::UpdateStatus::kFailedOffline));
+  ASSERT_TRUE(event);
+  VerifyDisplayString(event,
+                      "Browser can't check for updates. Try checking your "
+                      "internet connection.");
+  histogram_tester_.ExpectBucketCount(
+      "Settings.SafetyCheck.UpdatesResult",
+      SafetyCheckHandler::UpdateStatus::kFailedOffline, 1);
+}
+
 TEST_F(SafetyCheckHandlerTest, CheckUpdates_DestroyedOnJavascriptDisallowed) {
   EXPECT_FALSE(TestDestructionVersionUpdater::GetDestructorInvoked());
   safety_check_->SetVersionUpdaterForTesting(
diff --git a/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc b/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc
index 442bec19..bc0beca 100644
--- a/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc
+++ b/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "ui/webui/webui_allowlist_provider.h"
+#include "components/content_settings/core/browser/content_settings_observer.h"
 #include "ui/webui/webui_allowlist.h"
 
 #include <map>
@@ -126,6 +127,21 @@
   }
 }
 
+#if DCHECK_IS_ON()
+#define MAYBE_InvalidContentSetting InvalidContentSetting
+#else
+#define MAYBE_InvalidContentSetting DISABLED_InvalidContentSetting
+#endif
+TEST_F(WebUIAllowlistProviderTest, MAYBE_InvalidContentSetting) {
+  auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
+
+  EXPECT_DEATH_IF_SUPPORTED(
+      allowlist->RegisterAutoGrantedPermission(
+          url::Origin::Create(GURL("chrome://test/")),
+          ContentSettingsType::BLUETOOTH_GUARD, CONTENT_SETTING_DEFAULT),
+      std::string());
+}
+
 TEST_F(WebUIAllowlistProviderTest, AutoGrantPermissionIsPerProfile) {
   TestingProfileManager profile_manager(TestingBrowserProcess::GetGlobal());
   ASSERT_TRUE(profile_manager.SetUp());
@@ -154,3 +170,57 @@
             map2->GetContentSetting(url, url, ContentSettingsType::GEOLOCATION,
                                     std::string()));
 }
+
+class ContentSettingsChangeObserver : public content_settings::Observer {
+ public:
+  ContentSettingsChangeObserver() = default;
+  ContentSettingsChangeObserver(const ContentSettingsChangeObserver&) = delete;
+  void operator=(const ContentSettingsChangeObserver&) = delete;
+  ~ContentSettingsChangeObserver() override = default;
+
+  size_t change_counter() { return change_counter_; }
+
+  // content_settings::Observer:
+  void OnContentSettingChanged(
+      const ContentSettingsPattern& primary_pattern,
+      const ContentSettingsPattern& secondary_pattern,
+      ContentSettingsType content_type,
+      const std::string& resource_identifier) override {
+    change_counter_++;
+  }
+
+ private:
+  size_t change_counter_ = 0;
+};
+
+TEST_F(WebUIAllowlistProviderTest, OnlyNotifyOnChange) {
+  auto* map = GetHostContentSettingsMap(profile());
+  map->SetDefaultContentSetting(ContentSettingsType::BLUETOOTH_GUARD,
+                                CONTENT_SETTING_BLOCK);
+
+  ContentSettingsChangeObserver change_observer;
+  map->AddObserver(&change_observer);
+
+  const url::Origin origin1 = url::Origin::Create(GURL("chrome://test"));
+
+  auto* allowlist = WebUIAllowlist::GetOrCreate(profile());
+  allowlist->RegisterAutoGrantedPermission(
+      origin1, ContentSettingsType::BLUETOOTH_GUARD);
+  EXPECT_EQ(1U, change_observer.change_counter());
+
+  // Registering the same permission should not trigger OnContentSettingChanged.
+  allowlist->RegisterAutoGrantedPermission(
+      origin1, ContentSettingsType::BLUETOOTH_GUARD);
+  EXPECT_EQ(1U, change_observer.change_counter());
+
+  // Registering a different permission should trigger OnContentSettingChanged.
+  allowlist->RegisterAutoGrantedPermission(origin1,
+                                           ContentSettingsType::GEOLOCATION);
+  EXPECT_EQ(2U, change_observer.change_counter());
+
+  // Registering a different origin should trigger OnContentSettingChanged.
+  const url::Origin origin2 = url::Origin::Create(GURL("chrome://test2"));
+  allowlist->RegisterAutoGrantedPermission(origin2,
+                                           ContentSettingsType::GEOLOCATION);
+  EXPECT_EQ(3U, change_observer.change_counter());
+}
diff --git a/chrome/services/ipp_parser/ipp_parser_service.cc b/chrome/services/ipp_parser/ipp_parser_service.cc
index 7c62a90..8602471 100644
--- a/chrome/services/ipp_parser/ipp_parser_service.cc
+++ b/chrome/services/ipp_parser/ipp_parser_service.cc
@@ -16,7 +16,6 @@
   content::ServiceProcessHost::Launch<mojom::IppParser>(
       remote.InitWithNewPipeAndPassReceiver(),
       content::ServiceProcessHost::Options()
-          .WithSandboxType(service_manager::SandboxType::kUtility)
           .WithDisplayName(IDS_UTILITY_PROCESS_IPP_PARSER_SERVICE_NAME)
           .Pass());
   return remote;
diff --git a/chrome/services/media_gallery_util/public/cpp/media_parser_provider.cc b/chrome/services/media_gallery_util/public/cpp/media_parser_provider.cc
index 4c466b9..84160273 100644
--- a/chrome/services/media_gallery_util/public/cpp/media_parser_provider.cc
+++ b/chrome/services/media_gallery_util/public/cpp/media_parser_provider.cc
@@ -30,7 +30,6 @@
       remote_media_parser_factory_.BindNewPipeAndPassReceiver(),
       content::ServiceProcessHost::Options()
           .WithDisplayName(IDS_UTILITY_PROCESS_MEDIA_GALLERY_UTILITY_NAME)
-          .WithSandboxType(service_manager::SandboxType::kUtility)
           .Pass());
   remote_media_parser_factory_.set_disconnect_handler(base::BindOnce(
       &MediaParserProvider::OnConnectionError, base::Unretained(this)));
diff --git a/chrome/services/qrcode_generator/public/cpp/qrcode_generator_service.cc b/chrome/services/qrcode_generator/public/cpp/qrcode_generator_service.cc
index b46a176..09ee008 100644
--- a/chrome/services/qrcode_generator/public/cpp/qrcode_generator_service.cc
+++ b/chrome/services/qrcode_generator/public/cpp/qrcode_generator_service.cc
@@ -15,7 +15,6 @@
   // TODO: check default sandboxtype
   return content::ServiceProcessHost::Launch<mojom::QRCodeGeneratorService>(
       content::ServiceProcessHost::Options()
-          .WithSandboxType(service_manager::SandboxType::kUtility)
           .WithDisplayName(IDS_UTILITY_PROCESS_QRCODE_GENERATOR_SERVICE_NAME)
           .Pass());
 }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ad80471..33b878f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4274,6 +4274,7 @@
       "//chrome/services/sharing:unit_tests",
       "//components/chrome_cleaner/test:test_name_helper",
       "//components/feature_engagement/test:test_support",
+      "//components/safety_check:test_support",
       "//components/send_tab_to_self:test_support",
       "//components/signin/public/base:signin_buildflags",
       "//components/sync:test_support",
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 9d56f47a..8cfd027c 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -5420,19 +5420,7 @@
     "note": "This policy is deprecated in M82 and doesn't map to a pref value anymore."
   },
 
-  "MinimumChromeVersionEnforced": {
-    "os": ["chromeos"],
-    "policy_pref_mapping_test": [
-      {
-        "policies": {
-          "MinimumChromeVersionEnforced": [
-            {"chrome_version" : "82","warning_period" : 0, "eol_warning_period" : 14}
-          ]
-        },
-        "prefs": { "cros.min_version_enforced.chrome": {} }
-      }
-    ]
-  },
+  "MinimumChromeVersionEnforced": {},
 
   "DeviceLoginScreenAutoSelectCertificateForUrls": {},
 
@@ -6044,7 +6032,7 @@
   },
 
   "ForceNetworkInProcess": {
-    "note": "This policy is used directly through the policy service instead of through a pref."
+    "note": "This policy is deprecated."
   },
 
   "DeviceWilcoDtcAllowed": {
diff --git a/chrome/test/data/webui/settings/safety_check_page_test.js b/chrome/test/data/webui/settings/safety_check_page_test.js
index 5ec58831..1a72efd 100644
--- a/chrome/test/data/webui/settings/safety_check_page_test.js
+++ b/chrome/test/data/webui/settings/safety_check_page_test.js
@@ -5,7 +5,7 @@
 // clang-format off
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {HatsBrowserProxyImpl, LifetimeBrowserProxyImpl, MetricsBrowserProxyImpl, OpenWindowProxyImpl, PasswordManagerImpl, PasswordManagerProxy, Router, routes, SafetyCheckBrowserProxy, SafetyCheckBrowserProxyImpl, SafetyCheckCallbackConstants, SafetyCheckExtensionsStatus, SafetyCheckInteractions, SafetyCheckParentStatus, SafetyCheckPasswordsStatus, SafetyCheckSafeBrowsingStatus, SafetyCheckUpdatesStatus} from 'chrome://settings/settings.js';
+import {HatsBrowserProxyImpl, LifetimeBrowserProxyImpl, MetricsBrowserProxyImpl, OpenWindowProxyImpl, PasswordManagerImpl, PasswordManagerProxy, Router, routes, SafetyCheckBrowserProxy, SafetyCheckBrowserProxyImpl, SafetyCheckCallbackConstants, SafetyCheckExtensionsStatus, SafetyCheckIconStatus, SafetyCheckInteractions, SafetyCheckParentStatus, SafetyCheckPasswordsStatus, SafetyCheckSafeBrowsingStatus, SafetyCheckUpdatesStatus} from 'chrome://settings/settings.js';
 import {TestHatsBrowserProxy} from 'chrome://test/settings/test_hats_browser_proxy.js';
 import {TestLifetimeBrowserProxy} from 'chrome://test/settings/test_lifetime_browser_proxy.m.js';
 import {TestMetricsBrowserProxy} from 'chrome://test/settings/test_metrics_browser_proxy.js';
@@ -15,25 +15,109 @@
 
 // clang-format on
 
-suite('SafetyCheckUiTests', function() {
-  /** @type {?LifetimeBrowserProxy} */
-  let lifetimeBrowserProxy = null;
+const testDisplayString = 'Test display string';
+
+/**
+ * Fire a safety check parent event.
+ * @param {SafetyCheckParentStatus}
+ */
+function fireSafetyCheckParentEvent(state) {
+  const event = {};
+  event.newState = state;
+  event.displayString = testDisplayString;
+  webUIListenerCallback(SafetyCheckCallbackConstants.PARENT_CHANGED, event);
+}
+
+/**
+ * Fire a safety check updates event.
+ * @param {SafetyCheckUpdatesStatus}
+ */
+function fireSafetyCheckUpdatesEvent(state) {
+  const event = {};
+  event.newState = state;
+  event.displayString = testDisplayString;
+  webUIListenerCallback(SafetyCheckCallbackConstants.UPDATES_CHANGED, event);
+}
+
+/**
+ * Fire a safety check passwords event.
+ * @param {SafetyCheckPasswordsStatus}
+ */
+function fireSafetyCheckPasswordsEvent(state) {
+  const event = {};
+  event.newState = state;
+  event.displayString = testDisplayString;
+  event.passwordsButtonString = null;
+  webUIListenerCallback(SafetyCheckCallbackConstants.PASSWORDS_CHANGED, event);
+}
+
+/**
+ * Fire a safety check safe browsing event.
+ * @param {SafetyCheckSafeBrowsingStatus}
+ */
+function fireSafetyCheckSafeBrowsingEvent(state) {
+  const event = {};
+  event.newState = state;
+  event.displayString = testDisplayString;
+  webUIListenerCallback(
+      SafetyCheckCallbackConstants.SAFE_BROWSING_CHANGED, event);
+}
+
+/**
+ * Fire a safety check extensions event.
+ * @param {SafetyCheckExtensionsStatus}
+ */
+function fireSafetyCheckExtensionsEvent(state) {
+  const event = {};
+  event.newState = state;
+  event.displayString = testDisplayString;
+  webUIListenerCallback(SafetyCheckCallbackConstants.EXTENSIONS_CHANGED, event);
+}
+
+/**
+ * Verify that the safety check child element of the page has been configured
+ * as specified.
+ * @param {!{
+ *   page: !PolymerElement,
+ *   iconStatus: !SafetyCheckExtensionsStatus,
+ *   label: string,
+ *   buttonLabel: string|undefined,
+ *   buttonAriaLabel: string|undefined,
+ *   buttonClass: string|undefined,
+ *   managedIcon: string|undefined,
+ * }} destructured1
+ */
+function assertSafetyCheckChild({
+  page,
+  iconStatus,
+  label,
+  buttonLabel,
+  buttonAriaLabel,
+  buttonClass,
+  managedIcon
+}) {
+  const safetyCheckChild = page.$$('#safetyCheckChild');
+  assertTrue(safetyCheckChild.iconStatus === iconStatus);
+  assertTrue(safetyCheckChild.label === label);
+  assertTrue(safetyCheckChild.subLabel === testDisplayString);
+  assertTrue(!buttonLabel || safetyCheckChild.buttonLabel === buttonLabel);
+  assertTrue(
+      !buttonAriaLabel || safetyCheckChild.buttonAriaLabel === buttonAriaLabel);
+  assertTrue(!buttonClass || safetyCheckChild.buttonClass === buttonClass);
+  assertTrue(!!managedIcon === !!safetyCheckChild.managedIcon);
+}
+
+suite('SafetyCheckPageUiTests', function() {
   /** @type {settings.TestMetricsBrowserProxy} */
-  let metricsBrowserProxy;
-  /** @type {OpenWindowProxy} */
-  let openWindowProxy = null;
+  let metricsBrowserProxy = null;
   /** @type {SafetyCheckBrowserProxy} */
   let safetyCheckBrowserProxy = null;
   /** @type {SettingsBasicPageElement} */
-  let page;
+  let page = null;
 
   setup(function() {
-    lifetimeBrowserProxy = new TestLifetimeBrowserProxy();
-    LifetimeBrowserProxyImpl.instance_ = lifetimeBrowserProxy;
     metricsBrowserProxy = new TestMetricsBrowserProxy();
     MetricsBrowserProxyImpl.instance_ = metricsBrowserProxy;
-    openWindowProxy = new TestOpenWindowProxy();
-    OpenWindowProxyImpl.instance_ = openWindowProxy;
     safetyCheckBrowserProxy =
         TestBrowserProxy.fromClass(SafetyCheckBrowserProxy);
     safetyCheckBrowserProxy.setResultFor(
@@ -50,88 +134,6 @@
     page.remove();
   });
 
-  function fireSafetyCheckParentEvent(state) {
-    const event = {};
-    event.newState = state;
-    event.displayString = null;
-    webUIListenerCallback(SafetyCheckCallbackConstants.PARENT_CHANGED, event);
-  }
-
-  function fireSafetyCheckUpdatesEvent(state) {
-    const event = {};
-    event.newState = state;
-    event.displayString = null;
-    webUIListenerCallback(SafetyCheckCallbackConstants.UPDATES_CHANGED, event);
-  }
-
-  function fireSafetyCheckPasswordsEvent(state) {
-    const event = {};
-    event.newState = state;
-    event.displayString = null;
-    event.passwordsButtonString = null;
-    webUIListenerCallback(
-        SafetyCheckCallbackConstants.PASSWORDS_CHANGED, event);
-  }
-
-  function fireSafetyCheckSafeBrowsingEvent(state) {
-    const event = {};
-    event.newState = state;
-    event.displayString = null;
-    webUIListenerCallback(
-        SafetyCheckCallbackConstants.SAFE_BROWSING_CHANGED, event);
-  }
-
-  function fireSafetyCheckExtensionsEvent(state) {
-    const event = {};
-    event.newState = state;
-    event.displayString = null;
-    webUIListenerCallback(
-        SafetyCheckCallbackConstants.EXTENSIONS_CHANGED, event);
-  }
-
-  function assertIconStatusRunning(icon) {
-    assertTrue(icon.classList.contains('icon-blue'));
-    assertFalse(icon.classList.contains('icon-red'));
-    assertEquals('Running', icon.getAttribute('aria-label'));
-  }
-
-  function assertIconStatusSafe(icon) {
-    assertTrue(icon.classList.contains('icon-blue'));
-    assertFalse(icon.classList.contains('icon-red'));
-    assertEquals('Passed', icon.getAttribute('aria-label'));
-  }
-
-  function assertIconStatusInfo(icon) {
-    assertFalse(icon.classList.contains('icon-blue'));
-    assertFalse(icon.classList.contains('icon-red'));
-    assertEquals('Info', icon.getAttribute('aria-label'));
-  }
-
-  function assertIconStatusWarning(icon) {
-    assertFalse(icon.classList.contains('icon-blue'));
-    assertTrue(icon.classList.contains('icon-red'));
-    assertEquals('Warning', icon.getAttribute('aria-label'));
-  }
-
-  /**
-   * @return {!Promise}
-   */
-  async function expectExtensionsButtonClickActions() {
-    // User clicks review extensions button.
-    page.$$('#safetyCheckExtensionsButton').click();
-    // Ensure UMA is logged.
-    assertEquals(
-        SafetyCheckInteractions.SAFETY_CHECK_EXTENSIONS_REVIEW,
-        await metricsBrowserProxy.whenCalled(
-            'recordSafetyCheckInteractionHistogram'));
-    assertEquals(
-        'Settings.SafetyCheck.ReviewExtensions',
-        await metricsBrowserProxy.whenCalled('recordAction'));
-    // Ensure the browser proxy call is done.
-    assertEquals(
-        'chrome://extensions', await openWindowProxy.whenCalled('openURL'));
-  }
-
   /** Tests parent element and collapse.from start to completion */
   test('testParentAndCollapse', async function() {
     // Before the check, only the text button is present.
@@ -186,40 +188,193 @@
     page.$$('#safetyCheckParentButton').click();
     return testHatsBrowserProxy.whenCalled('tryShowSurvey');
   });
+});
 
-  test('updatesCheckingUiTest', function() {
+suite('SafetyCheckChildTests', function() {
+  /** @type {SettingsBasicPageElement} */
+  let page = null;
+
+  setup(function() {
+    PolymerTest.clearBody();
+    page = document.createElement('settings-safety-check-child');
+    document.body.appendChild(page);
+  });
+
+  teardown(function() {
+    page.remove();
+  });
+
+  test('testIconStatusRunning', function() {
+    page.iconStatus = SafetyCheckIconStatus.RUNNING;
+    flush();
+    const statusIconElem = page.$$('#statusIcon');
+    assertTrue(!!statusIconElem);
+    assertTrue(statusIconElem.classList.contains('icon-blue'));
+    assertFalse(statusIconElem.classList.contains('icon-red'));
+    assertEquals('Running', statusIconElem.getAttribute('aria-label'));
+  });
+
+  test('testIconStatusSafe', function() {
+    page.iconStatus = SafetyCheckIconStatus.SAFE;
+    flush();
+    const statusIconElem = page.$$('#statusIcon');
+    assertTrue(!!statusIconElem);
+    assertTrue(statusIconElem.classList.contains('icon-blue'));
+    assertFalse(statusIconElem.classList.contains('icon-red'));
+    assertEquals('Passed', statusIconElem.getAttribute('aria-label'));
+  });
+
+  test('testIconStatusInfo', function() {
+    page.iconStatus = SafetyCheckIconStatus.INFO;
+    flush();
+    const statusIconElem = page.$$('#statusIcon');
+    assertTrue(!!statusIconElem);
+    assertFalse(statusIconElem.classList.contains('icon-blue'));
+    assertFalse(statusIconElem.classList.contains('icon-red'));
+    assertEquals('Info', statusIconElem.getAttribute('aria-label'));
+  });
+
+  test('testIconStatusWarning', function() {
+    page.iconStatus = SafetyCheckIconStatus.WARNING;
+    flush();
+    const statusIconElem = page.$$('#statusIcon');
+    assertTrue(!!statusIconElem);
+    assertFalse(statusIconElem.classList.contains('icon-blue'));
+    assertTrue(statusIconElem.classList.contains('icon-red'));
+    assertEquals('Warning', statusIconElem.getAttribute('aria-label'));
+  });
+
+  test('testLabelText', function() {
+    page.label = 'Main label test text';
+    flush();
+    const label = page.$$('#label');
+    assertTrue(!!label);
+    assertEquals('Main label test text', label.textContent.trim());
+  });
+
+  test('testSubLabelText', function() {
+    page.subLabel = 'Sub label test text';
+    flush();
+    const subLabel = page.$$('#subLabel');
+    assertTrue(!!subLabel);
+    assertEquals('Sub label test text', subLabel.textContent.trim());
+  });
+
+  test('testSubLabelNoText', function() {
+    // sublabel not set -> empty sublabel in element
+    const subLabel = page.$$('#subLabel');
+    assertTrue(!!subLabel);
+    assertEquals('', subLabel.textContent.trim());
+  });
+
+  test('testButtonWithoutClass', function() {
+    page.buttonLabel = 'Button label';
+    page.buttonAriaLabel = 'Aria label';
+    flush();
+    const button = page.$$('#button');
+    assertTrue(!!button);
+    assertEquals('Button label', button.textContent.trim());
+    assertEquals('Aria label', button.getAttribute('aria-label'));
+    assertFalse(button.classList.contains('action-button'));
+  });
+
+  test('testButtonWithClass', function() {
+    page.buttonLabel = 'Button label';
+    page.buttonAriaLabel = 'Aria label';
+    page.buttonClass = 'action-button';
+    flush();
+    const button = page.$$('#button');
+    assertTrue(!!button);
+    assertEquals('Button label', button.textContent.trim());
+    assertEquals('Aria label', button.getAttribute('aria-label'));
+    assertTrue(button.classList.contains('action-button'));
+  });
+
+  test('testNoButton', function() {
+    // Button label not set -> no button.
+    assertFalse(!!page.$$('#button'));
+  });
+
+  test('testManagedIcon', function() {
+    page.managedIcon = 'cr20:domain';
+    flush();
+    assertTrue(!!page.$$('#managedIcon'));
+  });
+
+  test('testNoManagedIcon', function() {
+    // Managed icon not set -> no managed icon.
+    assertFalse(!!page.$$('#managedIcon'));
+  });
+});
+
+suite('SafetyCheckUpdatesElementUiTests', function() {
+  /** @type {?LifetimeBrowserProxy} */
+  let lifetimeBrowserProxy = null;
+  /** @type {settings.TestMetricsBrowserProxy} */
+  let metricsBrowserProxy = null;
+  /** @type {SettingsBasicPageElement} */
+  let page = null;
+
+  setup(function() {
+    lifetimeBrowserProxy = new TestLifetimeBrowserProxy();
+    LifetimeBrowserProxyImpl.instance_ = lifetimeBrowserProxy;
+    metricsBrowserProxy = new TestMetricsBrowserProxy();
+    MetricsBrowserProxyImpl.instance_ = metricsBrowserProxy;
+
+    PolymerTest.clearBody();
+    page = document.createElement('settings-safety-check-updates-element');
+    document.body.appendChild(page);
+    flush();
+  });
+
+  teardown(function() {
+    page.remove();
+  });
+
+  test('checkingUiTest', function() {
     fireSafetyCheckUpdatesEvent(SafetyCheckUpdatesStatus.CHECKING);
     flush();
-    assertFalse(!!page.$$('#safetyCheckUpdatesButton'));
-    assertFalse(!!page.$$('#safetyCheckUpdatesManagedIcon'));
-    assertIconStatusRunning(page.$$('#updatesIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.RUNNING,
+      label: 'Updates',
+    });
   });
 
-  test('updatesUpdatedUiTest', function() {
+  test('updatedUiTest', function() {
     fireSafetyCheckUpdatesEvent(SafetyCheckUpdatesStatus.UPDATED);
     flush();
-    assertFalse(!!page.$$('#safetyCheckUpdatesButton'));
-    assertFalse(!!page.$$('#safetyCheckUpdatesManagedIcon'));
-    assertIconStatusSafe(page.$$('#updatesIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.SAFE,
+      label: 'Updates',
+    });
   });
 
-  test('updatesUpdatingUiTest', function() {
+  test('updatingUiTest', function() {
     fireSafetyCheckUpdatesEvent(SafetyCheckUpdatesStatus.UPDATING);
     flush();
-    assertFalse(!!page.$$('#safetyCheckUpdatesButton'));
-    assertFalse(!!page.$$('#safetyCheckUpdatesManagedIcon'));
-    assertIconStatusRunning(page.$$('#updatesIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.RUNNING,
+      label: 'Updates',
+    });
   });
 
-  test('updatesRelaunchUiTest', async function() {
+  test('relaunchUiTest', async function() {
     fireSafetyCheckUpdatesEvent(SafetyCheckUpdatesStatus.RELAUNCH);
     flush();
-    assertTrue(!!page.$$('#safetyCheckUpdatesButton'));
-    assertFalse(!!page.$$('#safetyCheckUpdatesManagedIcon'));
-    assertIconStatusInfo(page.$$('#updatesIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.INFO,
+      label: 'Updates',
+      buttonLabel: page.i18n('aboutRelaunch'),
+      buttonAriaLabel: page.i18n('safetyCheckUpdatesButtonAriaLabel'),
+      buttonClass: 'action-button',
+    });
 
     // User clicks the relaunch button.
-    page.$$('#safetyCheckUpdatesButton').click();
+    page.$$('#safetyCheckChild').$$('#button').click();
     // Ensure UMA is logged.
     assertEquals(
         SafetyCheckInteractions.SAFETY_CHECK_UPDATES_RELAUNCH,
@@ -232,85 +387,106 @@
     return lifetimeBrowserProxy.whenCalled('relaunch');
   });
 
-  test('updatesDisabledByAdminUiTest', function() {
+  test('disabledByAdminUiTest', function() {
     fireSafetyCheckUpdatesEvent(SafetyCheckUpdatesStatus.DISABLED_BY_ADMIN);
     flush();
-    assertFalse(!!page.$$('#safetyCheckUpdatesButton'));
-    assertTrue(!!page.$$('#safetyCheckUpdatesManagedIcon'));
-    assertIconStatusInfo(page.$$('#updatesIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.INFO,
+      label: 'Updates',
+      managedIcon: true,
+    });
   });
 
-  test('updatesFailedOfflineUiTest', function() {
+  test('failedOfflineUiTest', function() {
     fireSafetyCheckUpdatesEvent(SafetyCheckUpdatesStatus.FAILED_OFFLINE);
     flush();
-    assertFalse(!!page.$$('#safetyCheckUpdatesButton'));
-    assertFalse(!!page.$$('#safetyCheckUpdatesManagedIcon'));
-    assertIconStatusInfo(page.$$('#updatesIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.INFO,
+      label: 'Updates',
+    });
   });
 
-  test('updatesFailedUiTest', function() {
+  test('failedUiTest', function() {
     fireSafetyCheckUpdatesEvent(SafetyCheckUpdatesStatus.FAILED);
     flush();
-    assertFalse(!!page.$$('#safetyCheckUpdatesButton'));
-    assertFalse(!!page.$$('#safetyCheckUpdatesManagedIcon'));
-    assertIconStatusWarning(page.$$('#updatesIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.WARNING,
+      label: 'Updates',
+    });
   });
 
-  test('updatesUnknownUiTest', function() {
+  test('unknownUiTest', function() {
     fireSafetyCheckUpdatesEvent(SafetyCheckUpdatesStatus.UNKNOWN);
     flush();
-    assertFalse(!!page.$$('#safetyCheckUpdatesButton'));
-    assertFalse(!!page.$$('#safetyCheckUpdatesManagedIcon'));
-    assertIconStatusInfo(page.$$('#updatesIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.INFO,
+      label: 'Updates',
+    });
+  });
+});
+
+suite('SafetyCheckPasswordsElementUiTests', function() {
+  /** @type {settings.TestMetricsBrowserProxy} */
+  let metricsBrowserProxy = null;
+  /** @type {SettingsBasicPageElement} */
+  let page = null;
+
+  setup(function() {
+    metricsBrowserProxy = new TestMetricsBrowserProxy();
+    MetricsBrowserProxyImpl.instance_ = metricsBrowserProxy;
+
+    PolymerTest.clearBody();
+    page = document.createElement('settings-safety-check-passwords-element');
+    document.body.appendChild(page);
+    flush();
   });
 
-  test('passwordsUiTest', function() {
-    // Iterate over all states
-    for (const state of Object.values(SafetyCheckPasswordsStatus)) {
-      fireSafetyCheckPasswordsEvent(state);
-      flush();
-
-      // Button is only visible in COMPROMISED state
-      assertEquals(
-          state === SafetyCheckPasswordsStatus.COMPROMISED,
-          !!page.$$('#safetyCheckPasswordsButton'));
-
-      // Check that icon status is the correct one for this password status.
-      switch (state) {
-        case SafetyCheckPasswordsStatus.CHECKING:
-          assertIconStatusRunning(page.$$('#passwordsIcon'));
-          break;
-        case SafetyCheckPasswordsStatus.SAFE:
-          assertIconStatusSafe(page.$$('#passwordsIcon'));
-          break;
-        case SafetyCheckPasswordsStatus.COMPROMISED:
-          assertIconStatusWarning(page.$$('#passwordsIcon'));
-          break;
-        case SafetyCheckPasswordsStatus.OFFLINE:
-        case SafetyCheckPasswordsStatus.NO_PASSWORDS:
-        case SafetyCheckPasswordsStatus.SIGNED_OUT:
-        case SafetyCheckPasswordsStatus.QUOTA_LIMIT:
-        case SafetyCheckPasswordsStatus.ERROR:
-          assertIconStatusInfo(page.$$('#passwordsIcon'));
-          break;
-        default:
-          assertNotReached();
-      }
-    }
+  teardown(function() {
+    page.remove();
   });
 
-  test('passwordsCompromisedUiTest', async function() {
+  test('passwordCheckingUiTest', function() {
+    fireSafetyCheckPasswordsEvent(SafetyCheckUpdatesStatus.CHECKING);
+    flush();
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.RUNNING,
+      label: 'Passwords',
+    });
+  });
+
+  test('passwordSafeUiTest', function() {
+    fireSafetyCheckPasswordsEvent(SafetyCheckPasswordsStatus.SAFE);
+    flush();
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.SAFE,
+      label: 'Passwords',
+    });
+  });
+
+  test('passwordCompromisedUiTest', async function() {
+    fireSafetyCheckPasswordsEvent(SafetyCheckPasswordsStatus.COMPROMISED);
+    flush();
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.WARNING,
+      label: 'Passwords',
+      buttonLabel: 'Review',
+      buttonAriaLabel: 'Review passwords',
+      buttonClass: 'action-button',
+    });
+
     loadTimeData.overrideValues({enablePasswordCheck: true});
     const passwordManager = new TestPasswordManagerProxy();
     PasswordManagerImpl.instance_ = passwordManager;
 
-    fireSafetyCheckPasswordsEvent(SafetyCheckPasswordsStatus.COMPROMISED);
-    flush();
-    assertTrue(!!page.$$('#safetyCheckPasswordsButton'));
-    assertIconStatusWarning(page.$$('#passwordsIcon'));
-
     // User clicks the manage passwords button.
-    page.$$('#safetyCheckPasswordsButton').click();
+    page.$$('#safetyCheckChild').$$('#button').click();
     // Ensure UMA is logged.
     assertEquals(
         SafetyCheckInteractions.SAFETY_CHECK_PASSWORDS_MANAGE,
@@ -330,41 +506,100 @@
         PasswordManagerProxy.PasswordCheckReferrer.SAFETY_CHECK, referrer);
   });
 
+  test('passwordInfoStatesUiTest', function() {
+    // Iterate over all states
+    for (const state of Object.values(SafetyCheckPasswordsStatus)) {
+      fireSafetyCheckPasswordsEvent(state);
+      flush();
+
+      // Check that icon status is the correct one for this password status.
+      switch (state) {
+        case SafetyCheckPasswordsStatus.OFFLINE:
+        case SafetyCheckPasswordsStatus.NO_PASSWORDS:
+        case SafetyCheckPasswordsStatus.SIGNED_OUT:
+        case SafetyCheckPasswordsStatus.QUOTA_LIMIT:
+        case SafetyCheckPasswordsStatus.ERROR:
+          assertSafetyCheckChild({
+            page: page,
+            iconStatus: SafetyCheckIconStatus.INFO,
+            label: 'Passwords',
+          });
+          break;
+        default:
+          // Not covered by this test.
+          break;
+      }
+    }
+  });
+});
+
+suite('SafetyCheckSafeBrowsingElementUiTests', function() {
+  /** @type {settings.TestMetricsBrowserProxy} */
+  let metricsBrowserProxy = null;
+  /** @type {SettingsBasicPageElement} */
+  let page = null;
+
+  setup(function() {
+    metricsBrowserProxy = new TestMetricsBrowserProxy();
+    MetricsBrowserProxyImpl.instance_ = metricsBrowserProxy;
+
+    PolymerTest.clearBody();
+    page =
+        document.createElement('settings-safety-check-safe-browsing-element');
+    document.body.appendChild(page);
+    flush();
+  });
+
+  teardown(function() {
+    page.remove();
+  });
+
   test('safeBrowsingCheckingUiTest', function() {
     fireSafetyCheckSafeBrowsingEvent(SafetyCheckSafeBrowsingStatus.CHECKING);
     flush();
-    assertFalse(!!page.$$('#safetyCheckSafeBrowsingButton'));
-    assertFalse(!!page.$$('#safetyCheckSafeBrowsingManagedIcon'));
-    assertIconStatusRunning(page.$$('#safeBrowsingIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.RUNNING,
+      label: 'Safe Browsing',
+    });
   });
 
   test('safeBrowsingEnabledStandardUiTest', function() {
     fireSafetyCheckSafeBrowsingEvent(
         SafetyCheckSafeBrowsingStatus.ENABLED_STANDARD);
     flush();
-    assertFalse(!!page.$$('#safetyCheckSafeBrowsingButton'));
-    assertFalse(!!page.$$('#safetyCheckSafeBrowsingManagedIcon'));
-    assertIconStatusSafe(page.$$('#safeBrowsingIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.SAFE,
+      label: 'Safe Browsing',
+    });
   });
 
   test('safeBrowsingEnabledEnhancedUiTest', function() {
     fireSafetyCheckSafeBrowsingEvent(
         SafetyCheckSafeBrowsingStatus.ENABLED_ENHANCED);
     flush();
-    assertFalse(!!page.$$('#safetyCheckSafeBrowsingButton'));
-    assertFalse(!!page.$$('#safetyCheckSafeBrowsingManagedIcon'));
-    assertIconStatusSafe(page.$$('#safeBrowsingIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.SAFE,
+      label: 'Safe Browsing',
+    });
   });
 
   test('safeBrowsingDisabledUiTest', async function() {
     fireSafetyCheckSafeBrowsingEvent(SafetyCheckSafeBrowsingStatus.DISABLED);
     flush();
-    assertTrue(!!page.$$('#safetyCheckSafeBrowsingButton'));
-    assertFalse(!!page.$$('#safetyCheckSafeBrowsingManagedIcon'));
-    assertIconStatusInfo(page.$$('#safeBrowsingIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.INFO,
+      label: 'Safe Browsing',
+      buttonLabel: 'Manage',
+      buttonAriaLabel: 'Manage Safe Browsing',
+      buttonClass: 'action-button',
+    });
 
     // User clicks the manage safe browsing button.
-    page.$$('#safetyCheckSafeBrowsingButton').click();
+    page.$$('#safetyCheckChild').$$('#button').click();
     // Ensure UMA is logged.
     assertEquals(
         SafetyCheckInteractions.SAFETY_CHECK_SAFE_BROWSING_MANAGE,
@@ -381,53 +616,112 @@
     fireSafetyCheckSafeBrowsingEvent(
         SafetyCheckSafeBrowsingStatus.DISABLED_BY_ADMIN);
     flush();
-    assertFalse(!!page.$$('#safetyCheckSafeBrowsingButton'));
-    assertTrue(!!page.$$('#safetyCheckSafeBrowsingManagedIcon'));
-    assertIconStatusInfo(page.$$('#safeBrowsingIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.INFO,
+      label: 'Safe Browsing',
+      managedIcon: true,
+    });
   });
 
   test('safeBrowsingDisabledByExtensionUiTest', function() {
     fireSafetyCheckSafeBrowsingEvent(
         SafetyCheckSafeBrowsingStatus.DISABLED_BY_EXTENSION);
     flush();
-    assertFalse(!!page.$$('#safetyCheckSafeBrowsingButton'));
-    assertTrue(!!page.$$('#safetyCheckSafeBrowsingManagedIcon'));
-    assertIconStatusInfo(page.$$('#safeBrowsingIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.INFO,
+      label: 'Safe Browsing',
+      managedIcon: true,
+    });
   });
+});
+
+suite('SafetyCheckExtensionsElementUiTests', function() {
+  /** @type {settings.TestMetricsBrowserProxy} */
+  let metricsBrowserProxy = null;
+  /** @type {OpenWindowProxy} */
+  let openWindowProxy = null;
+  /** @type {SettingsBasicPageElement} */
+  let page = null;
+
+  setup(function() {
+    metricsBrowserProxy = new TestMetricsBrowserProxy();
+    MetricsBrowserProxyImpl.instance_ = metricsBrowserProxy;
+    openWindowProxy = new TestOpenWindowProxy();
+    OpenWindowProxyImpl.instance_ = openWindowProxy;
+
+    PolymerTest.clearBody();
+    page = document.createElement('settings-safety-check-extensions-element');
+    document.body.appendChild(page);
+    flush();
+  });
+
+  teardown(function() {
+    page.remove();
+  });
+
+  /**
+   * @return {!Promise}
+   */
+  async function expectExtensionsButtonClickActions() {
+    // User clicks review extensions button.
+    page.$$('#safetyCheckChild').$$('#button').click();
+    // Ensure UMA is logged.
+    assertEquals(
+        SafetyCheckInteractions.SAFETY_CHECK_EXTENSIONS_REVIEW,
+        await metricsBrowserProxy.whenCalled(
+            'recordSafetyCheckInteractionHistogram'));
+    assertEquals(
+        'Settings.SafetyCheck.ReviewExtensions',
+        await metricsBrowserProxy.whenCalled('recordAction'));
+    // Ensure the browser proxy call is done.
+    assertEquals(
+        'chrome://extensions', await openWindowProxy.whenCalled('openURL'));
+  }
 
   test('extensionsCheckingUiTest', function() {
     fireSafetyCheckExtensionsEvent(SafetyCheckExtensionsStatus.CHECKING);
     flush();
-    assertFalse(!!page.$$('#safetyCheckExtensionsButton'));
-    assertFalse(!!page.$$('#safetyCheckExtensionsManagedIcon'));
-    assertIconStatusRunning(page.$$('#extensionsIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.RUNNING,
+      label: 'Extensions',
+    });
   });
 
   test('extensionsErrorUiTest', function() {
     fireSafetyCheckExtensionsEvent(SafetyCheckExtensionsStatus.ERROR);
     flush();
-    assertFalse(!!page.$$('#safetyCheckExtensionsButton'));
-    assertFalse(!!page.$$('#safetyCheckExtensionsManagedIcon'));
-    assertIconStatusInfo(page.$$('#extensionsIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.INFO,
+      label: 'Extensions',
+    });
   });
 
   test('extensionsSafeUiTest', function() {
     fireSafetyCheckExtensionsEvent(
         SafetyCheckExtensionsStatus.NO_BLOCKLISTED_EXTENSIONS);
     flush();
-    assertFalse(!!page.$$('#safetyCheckExtensionsButton'));
-    assertFalse(!!page.$$('#safetyCheckExtensionsManagedIcon'));
-    assertIconStatusSafe(page.$$('#extensionsIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.SAFE,
+      label: 'Extensions',
+    });
   });
 
   test('extensionsBlocklistedOffUiTest', function() {
     fireSafetyCheckExtensionsEvent(
         SafetyCheckExtensionsStatus.BLOCKLISTED_ALL_DISABLED);
     flush();
-    assertTrue(!!page.$$('#safetyCheckExtensionsButton'));
-    assertFalse(!!page.$$('#safetyCheckExtensionsManagedIcon'));
-    assertIconStatusSafe(page.$$('#extensionsIcon'));
-
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.SAFE,
+      label: 'Extensions',
+      buttonLabel: 'Review',
+      buttonAriaLabel: 'Review extensions',
+    });
     return expectExtensionsButtonClickActions();
   });
 
@@ -435,10 +729,14 @@
     fireSafetyCheckExtensionsEvent(
         SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_USER);
     flush();
-    assertTrue(!!page.$$('#safetyCheckExtensionsButton'));
-    assertFalse(!!page.$$('#safetyCheckExtensionsManagedIcon'));
-    assertIconStatusWarning(page.$$('#extensionsIcon'));
-
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.WARNING,
+      label: 'Extensions',
+      buttonLabel: 'Review',
+      buttonAriaLabel: 'Review extensions',
+      buttonClass: 'action-button',
+    });
     return expectExtensionsButtonClickActions();
   });
 
@@ -446,10 +744,15 @@
     fireSafetyCheckExtensionsEvent(
         SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_SOME_BY_USER);
     flush();
-    assertTrue(!!page.$$('#safetyCheckExtensionsButton'));
-    assertFalse(!!page.$$('#safetyCheckExtensionsManagedIcon'));
-    assertIconStatusWarning(page.$$('#extensionsIcon'));
-
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.WARNING,
+      label: 'Extensions',
+      buttonLabel: 'Review',
+      buttonAriaLabel: 'Review extensions',
+      buttonClass: 'action-button',
+      managedIcon: false,
+    });
     return expectExtensionsButtonClickActions();
   });
 
@@ -457,8 +760,11 @@
     fireSafetyCheckExtensionsEvent(
         SafetyCheckExtensionsStatus.BLOCKLISTED_REENABLED_ALL_BY_ADMIN);
     flush();
-    assertFalse(!!page.$$('#safetyCheckExtensionsButton'));
-    assertTrue(!!page.$$('#safetyCheckExtensionsManagedIcon'));
-    assertIconStatusInfo(page.$$('#extensionsIcon'));
+    assertSafetyCheckChild({
+      page: page,
+      iconStatus: SafetyCheckIconStatus.INFO,
+      label: 'Extensions',
+      managedIcon: true,
+    });
   });
 });
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 637fe56..7c765b21 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13090.0.0
\ No newline at end of file
+13091.0.0
\ No newline at end of file
diff --git a/chromeos/components/sample_system_web_app_ui/sample_system_web_app_ui.cc b/chromeos/components/sample_system_web_app_ui/sample_system_web_app_ui.cc
index 12fc1cd..cedd039 100644
--- a/chromeos/components/sample_system_web_app_ui/sample_system_web_app_ui.cc
+++ b/chromeos/components/sample_system_web_app_ui/sample_system_web_app_ui.cc
@@ -15,7 +15,7 @@
 #include "content/public/common/url_constants.h"
 
 namespace chromeos {
-
+namespace {
 content::WebUIDataSource* CreateUntrustedSampleSystemWebAppDataSource() {
   content::WebUIDataSource* untrusted_source =
       content::WebUIDataSource::Create(kChromeUIUntrustedSampleSystemWebAppURL);
@@ -26,6 +26,7 @@
   untrusted_source->AddFrameAncestor(GURL(kChromeUISampleSystemWebAppURL));
   return untrusted_source;
 }
+}  // namespace
 
 SampleSystemWebAppUI::SampleSystemWebAppUI(content::WebUI* web_ui)
     : ui::MojoWebUIController(web_ui) {
diff --git a/chromeos/constants/chromeos_pref_names.cc b/chromeos/constants/chromeos_pref_names.cc
index 281259a..c41c642b8c 100644
--- a/chromeos/constants/chromeos_pref_names.cc
+++ b/chromeos/constants/chromeos_pref_names.cc
@@ -69,7 +69,7 @@
 const char kSamlPasswordChangeUrl[] = "saml.password_change_url";
 
 // Boolean pref indicating whether a user has enabled the display password
-// button on the login/lock screen. This can be set to false by policy.
+// button on the login/lock screen.
 const char kLoginDisplayPasswordButtonEnabled[] =
     "login_display_password_button_enabled";
 
diff --git a/components/autofill/OWNERS b/components/autofill/OWNERS
index 2e5e8da..13ff42e 100644
--- a/components/autofill/OWNERS
+++ b/components/autofill/OWNERS
@@ -3,7 +3,7 @@
 dvadym@chromium.org
 estade@chromium.org
 ftirelo@chromium.org
-koerber@chromium.org
+koerber@google.com
 kolos@chromium.org
 mahmadi@chromium.org
 rogerm@chromium.org
diff --git a/components/autofill/core/browser/autofill_download_manager.cc b/components/autofill/core/browser/autofill_download_manager.cc
index 999d1452..92847181 100644
--- a/components/autofill/core/browser/autofill_download_manager.cc
+++ b/components/autofill/core/browser/autofill_download_manager.cc
@@ -98,7 +98,7 @@
 };
 
 const char kDefaultAutofillServerURL[] =
-    "https://clients1.google.com/tbproxy/af/";
+    "https://content-autofill.googleapis.com/";
 
 // The default number of days after which to reset the registry of autofill
 // events for which an upload has been sent.
@@ -810,7 +810,8 @@
   std::string method = "POST";
 
   if (request_data.request_type == AutofillDownloadManager::REQUEST_QUERY) {
-    if (GetPayloadLength(request_data.payload) <= kMaxAPIQueryGetSize) {
+    if (GetPayloadLength(request_data.payload) <= kMaxAPIQueryGetSize &&
+        base::FeatureList::IsEnabled(features::kAutofillCacheQueryResponses)) {
       resource_id = request_data.payload;
       method = "GET";
       UMA_HISTOGRAM_BOOLEAN("Autofill.Query.ApiUrlIsTooLong", false);
diff --git a/components/autofill/core/browser/autofill_download_manager_unittest.cc b/components/autofill/core/browser/autofill_download_manager_unittest.cc
index a3225b9..37a8199 100644
--- a/components/autofill/core/browser/autofill_download_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_download_manager_unittest.cc
@@ -579,10 +579,11 @@
     // This is the URL we expect to query the API. The sub-path right after
     // "/page" corresponds to the serialized AutofillPageQueryRequest proto
     // (that we filled forms in) encoded in base64. The Autofill
-    // https://clients1.google.com/ domain URL corresponds to the default domain
-    // used by the download manager, which is invalid, but good for testing.
+    // https://content-autofill.googleapis.com/ domain URL corresponds to the
+    // default domain used by the download manager, which is invalid, but good
+    // for testing.
     const std::string expected_url =
-        R"(https://clients1.google.com/v1/pages/(.+)\?alt=proto)";
+        R"(https://content-autofill.googleapis.com/v1/pages/(.+)\?alt=proto)";
     std::string encoded_request;
     ASSERT_TRUE(re2::RE2::FullMatch(request->request.url.spec(), expected_url,
                                     &encoded_request));
@@ -676,7 +677,7 @@
       test_url_loader_factory_.GetPendingRequest(0);
   // Verify that the POST URL is used when request data too large.
   const std::string expected_url = {
-      "https://clients1.google.com/v1/pages:get?alt=proto"};
+      "https://content-autofill.googleapis.com/v1/pages:get?alt=proto"};
   // Verify API key header.
   EXPECT_EQ(request->request.url, expected_url);
   {
@@ -775,11 +776,11 @@
       test_url_loader_factory_.GetPendingRequest(0);
 
   // This is the URL we expect to upload votes to the API. The Autofill
-  // https://clients1.google.com/ domain URL corresponds to the
+  // https://content-autofill.googleapis.com/ domain URL corresponds to the
   // default one used by the download manager. Request upload data is in the
   // payload when uploading.
   const std::string expected_url =
-      "https://clients1.google.com/v1/forms:vote?alt=proto";
+      "https://content-autofill.googleapis.com/v1/forms:vote?alt=proto";
   EXPECT_EQ(request->request.url, expected_url);
   std::string api_key_header_value;
   EXPECT_TRUE(request->request.headers.GetHeader("X-Goog-Api-Key",
@@ -1349,7 +1350,7 @@
                             base::Unretained(this)));
     ASSERT_TRUE(server_.Start());
 
-    GURL autofill_server_url(server_.base_url().Resolve("/tbproxy/af/"));
+    GURL autofill_server_url(server_.base_url());
     ASSERT_TRUE(autofill_server_url.is_valid());
 
     // Intialize the autofill driver.
@@ -1410,22 +1411,17 @@
     run_loop_->QuitWhenIdle();
   }
 
-  // Helper to extract the value of a query param. Returns "*** not found ***"
-  // if the requested query param is not in the query string.
-  std::string GetQueryParam(const std::string& query_str,
-                            const std::string& param_name) {
-    url::Component query(0, query_str.length());
-    url::Component key, value;
-    while (url::ExtractQueryKeyValue(query_str.c_str(), &query, &key, &value)) {
-      base::StringPiece key_string(query_str.c_str() + key.begin, key.len);
-      base::StringPiece param_text(query_str.c_str() + value.begin, value.len);
-      std::string param_value;
-      if (key_string == param_name &&
-          base::Base64UrlDecode(param_text,
-                                base::Base64UrlDecodePolicy::REQUIRE_PADDING,
-                                &param_value)) {
-        return param_value;
-      }
+  // Helper to extract the value passed to a lookup in the URL. Returns "*** not
+  // found ***" if the the data cannot be decoded.
+  std::string GetLookupContent(const std::string& query_path) {
+    if (query_path.find("/v1/pages/") == std::string::npos)
+      return "*** not found ***";
+    std::string payload = query_path.substr(strlen("/v1/pages/"));
+    std::string decoded_payload;
+    if (base::Base64UrlDecode(payload,
+                              base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+                              &decoded_payload)) {
+      return decoded_payload;
     }
     return "*** not found ***";
   }
@@ -1434,12 +1430,15 @@
     GURL absolute_url = server_.GetURL(request.relative_url);
     ++call_count_;
 
-    if (absolute_url.path() == "/tbproxy/af/query") {
+    if (absolute_url.path().find("/v1/pages") == 0) {
       payloads_.push_back(!request.content.empty()
                               ? request.content
-                              : GetQueryParam(absolute_url.query(), "q"));
-      AutofillQueryResponseContents proto;
-      proto.add_field()->set_overall_type_prediction(NAME_FIRST);
+                              : GetLookupContent(absolute_url.path()));
+      AutofillQueryResponse proto;
+      proto.add_form_suggestions()
+          ->add_field_suggestions()
+          ->add_predictions()
+          ->set_type(NAME_FIRST);
 
       auto response = std::make_unique<BasicHttpResponse>();
       response->set_code(net::HTTP_OK);
@@ -1454,7 +1453,7 @@
       return response;
     }
 
-    if (absolute_url.path() == "/tbproxy/af/upload") {
+    if (absolute_url.path() == "/v1/forms:vote") {
       payloads_.push_back(request.content);
       auto response = std::make_unique<BasicHttpResponse>();
       response->set_code(net::HTTP_OK);
@@ -1657,8 +1656,9 @@
     histogram.ExpectBucketCount("Autofill.Query.WasInCache", CACHE_MISS, 1);
 
     ASSERT_EQ(1u, payloads_.size());
-    AutofillQueryContents query_contents;
+    AutofillPageQueryRequest query_contents;
     ASSERT_TRUE(query_contents.ParseFromString(payloads_[0]));
+
     ASSERT_EQ(2, query_contents.experiments_size());
     EXPECT_EQ(3312923, query_contents.experiments(0));
     EXPECT_EQ(3314883, query_contents.experiments(1));
@@ -1789,30 +1789,30 @@
 
   // We should have intercepted exactly on query request. Parse it.
   ASSERT_EQ(1u, payloads_.size());
-  AutofillQueryContents query;
+  AutofillPageQueryRequest query;
   ASSERT_TRUE(query.ParseFromString(payloads_.front()));
 
   // Validate that we have one form in the query.
-  ASSERT_EQ(query.form_size(), 1);
-  const auto& query_form = query.form(0);
+  ASSERT_EQ(query.forms_size(), 1);
+  const auto& query_form = query.forms(0);
 
   // The form should have metadata, and the metadata value should be equal
   // those initialized above.
-  ASSERT_TRUE(query_form.has_form_metadata());
-  EXPECT_EQ(UTF8ToUTF16(query_form.form_metadata().id().encoded_bits()),
+  ASSERT_TRUE(query_form.has_metadata());
+  EXPECT_EQ(UTF8ToUTF16(query_form.metadata().id().encoded_bits()),
             form.id_attribute);
-  EXPECT_EQ(UTF8ToUTF16(query_form.form_metadata().name().encoded_bits()),
+  EXPECT_EQ(UTF8ToUTF16(query_form.metadata().name().encoded_bits()),
             form.name_attribute);
 
   // The form should have 3 fields, and their metadata value should be equal
   // those initialized above.
-  ASSERT_EQ(3, query_form.field_size());
-  ASSERT_EQ(static_cast<int>(form.fields.size()), query_form.field_size());
-  for (int i = 0; i < query_form.field_size(); ++i) {
-    const auto& query_field = query_form.field(i);
+  ASSERT_EQ(3, query_form.fields_size());
+  ASSERT_EQ(static_cast<int>(form.fields.size()), query_form.fields_size());
+  for (int i = 0; i < query_form.fields_size(); ++i) {
+    const auto& query_field = query_form.fields(i);
     const auto& form_field = form.fields[i];
-    ASSERT_TRUE(query_field.has_field_metadata());
-    const auto& meta = query_field.field_metadata();
+    ASSERT_TRUE(query_field.has_metadata());
+    const auto& meta = query_field.metadata();
     EXPECT_EQ(UTF8ToUTF16(meta.id().encoded_bits()), form_field.id_attribute);
     EXPECT_EQ(UTF8ToUTF16(meta.name().encoded_bits()),
               form_field.name_attribute);
@@ -1889,22 +1889,22 @@
 
   // We should have intercepted exactly on query request. Parse it.
   ASSERT_EQ(1u, payloads_.size());
-  AutofillQueryContents query;
+  AutofillPageQueryRequest query;
   ASSERT_TRUE(query.ParseFromString(payloads_.front()));
 
   // Validate that we have one form in the query.
-  ASSERT_EQ(query.form_size(), 1);
-  const auto& query_form = query.form(0);
+  ASSERT_EQ(query.forms_size(), 1);
+  const auto& query_form = query.forms(0);
 
   // There should be no encoded metadata for the form.
-  EXPECT_FALSE(query_form.has_form_metadata());
+  EXPECT_FALSE(query_form.has_metadata());
 
   // There should be three fields, none of which have encoded metadata.
-  ASSERT_EQ(3, query_form.field_size());
-  ASSERT_EQ(static_cast<int>(form.fields.size()), query_form.field_size());
-  for (int i = 0; i < query_form.field_size(); ++i) {
-    const auto& query_field = query_form.field(i);
-    EXPECT_FALSE(query_field.has_field_metadata());
+  ASSERT_EQ(3, query_form.fields_size());
+  ASSERT_EQ(static_cast<int>(form.fields.size()), query_form.fields_size());
+  for (int i = 0; i < query_form.fields_size(); ++i) {
+    const auto& query_field = query_form.fields(i);
+    EXPECT_FALSE(query_field.has_metadata());
   }
 }
 
@@ -1994,8 +1994,10 @@
                                         true, 1);
 
     ASSERT_EQ(1u, payloads_.size());
-    AutofillUploadContents upload;
-    ASSERT_TRUE(upload.ParseFromString(payloads_.front()));
+    AutofillUploadRequest request;
+    ASSERT_TRUE(request.ParseFromString(payloads_.front()));
+    ASSERT_TRUE(request.has_upload());
+    const AutofillUploadContents& upload = request.upload();
     EXPECT_EQ(upload.language(), form_structure.page_language());
     ASSERT_TRUE(upload.has_randomized_form_metadata());
     EXPECT_TRUE(upload.randomized_form_metadata().has_id());
@@ -2138,8 +2140,10 @@
     // The last middle two uploads were marked as throttle-able.
     ASSERT_EQ(4u, payloads_.size());
     for (size_t i = 0; i < payloads_.size(); ++i) {
-      AutofillUploadContents upload_contents;
-      ASSERT_TRUE(upload_contents.ParseFromString(payloads_[i]));
+      AutofillUploadRequest request;
+      ASSERT_TRUE(request.ParseFromString(payloads_[i]));
+      ASSERT_TRUE(request.has_upload());
+      const AutofillUploadContents& upload_contents = request.upload();
       EXPECT_EQ(upload_contents.was_throttleable(), (i == 1 || i == 2))
           << "Wrong was_throttleable value for upload " << i;
       EXPECT_FALSE(upload_contents.has_randomized_form_metadata());
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index 8fced3b..69b9c66 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -4671,7 +4671,18 @@
 
 // Test that OnLoadedServerPredictions can obtain the FormStructure with the
 // signature of the queried form and apply type predictions.
-TEST_F(AutofillManagerTest, OnLoadedServerPredictions) {
+TEST_F(AutofillManagerTest, OnLoadedServerPredictionsFromLegacyServer) {
+  // Set features.
+  // This entire test can be deleted because we have
+  // OnLoadedServerPredictionsAPI.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      // Enabled
+      {},
+      // Disabled
+      // We want to query the legacy server rather than the API server.
+      {features::kAutofillUseApi});
+
   // Set up our form data.
   FormData form;
   test::CreateTestAddressFormData(&form);
@@ -4868,18 +4879,22 @@
   autofill_manager_->AddSeenFormStructure(
       std::unique_ptr<TestFormStructure>(form_structure));
 
-  AutofillQueryResponseContents response;
-  response.add_field()->set_overall_type_prediction(3);
+  AutofillQueryResponse response;
+  auto* form_suggestion = response.add_form_suggestions();
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(3);
   for (int i = 0; i < 7; ++i) {
-    response.add_field()->set_overall_type_prediction(0);
+    form_suggestion->add_field_suggestions()->set_primary_type_prediction(0);
   }
-  response.add_field()->set_overall_type_prediction(3);
-  response.add_field()->set_overall_type_prediction(2);
-  response.add_field()->set_overall_type_prediction(61);
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(3);
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(2);
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(61);
 
   std::string response_string;
   ASSERT_TRUE(response.SerializeToString(&response_string));
 
+  std::string response_string_base64;
+  base::Base64Encode(response_string, &response_string_base64);
+
   std::vector<std::string> signatures;
   signatures.push_back(form_structure->FormSignatureAsStr());
 
@@ -4887,7 +4902,7 @@
   autofill_manager_->Reset();
 
   base::HistogramTester histogram_tester;
-  autofill_manager_->OnLoadedServerPredictionsForTest(response_string,
+  autofill_manager_->OnLoadedServerPredictionsForTest(response_string_base64,
                                                       signatures);
 
   // Verify that FormStructure::ParseQueryResponse was NOT called.
@@ -4924,22 +4939,30 @@
   autofill_manager_->AddSeenFormStructure(
       std::unique_ptr<TestFormStructure>(form_structure));
 
-  AutofillQueryResponseContents response;
-  response.add_field()->set_overall_type_prediction(CREDIT_CARD_NAME_FIRST);
-  response.add_field()->set_overall_type_prediction(CREDIT_CARD_NAME_LAST);
-  response.add_field()->set_overall_type_prediction(CREDIT_CARD_NUMBER);
-  response.add_field()->set_overall_type_prediction(CREDIT_CARD_EXP_MONTH);
-  response.add_field()->set_overall_type_prediction(
+  AutofillQueryResponse response;
+  auto* form_suggestion = response.add_form_suggestions();
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(
+      CREDIT_CARD_NAME_FIRST);
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(
+      CREDIT_CARD_NAME_LAST);
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(
+      CREDIT_CARD_NUMBER);
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(
+      CREDIT_CARD_EXP_MONTH);
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(
       CREDIT_CARD_EXP_4_DIGIT_YEAR);
 
   std::string response_string;
   ASSERT_TRUE(response.SerializeToString(&response_string));
 
+  std::string response_string_base64;
+  base::Base64Encode(response_string, &response_string_base64);
+
   std::vector<std::string> signatures;
   signatures.push_back(form_structure->FormSignatureAsStr());
 
   base::HistogramTester histogram_tester;
-  autofill_manager_->OnLoadedServerPredictionsForTest(response_string,
+  autofill_manager_->OnLoadedServerPredictionsForTest(response_string_base64,
                                                       signatures);
   // Verify that FormStructure::ParseQueryResponse was called (here and below).
   histogram_tester.ExpectBucketCount("Autofill.ServerQueryResponse",
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 215c926..4da42d39 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/base64.h"
 #include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/metrics/metrics_hashes.h"
@@ -2623,24 +2624,31 @@
                             std::move(form_structure))
                   .second);
 
-  AutofillQueryResponseContents response;
+  AutofillQueryResponse response;
+  auto* form_suggestion = response.add_form_suggestions();
   // Server response will match with autocomplete.
-  response.add_field()->set_overall_type_prediction(NAME_LAST);
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(
+      NAME_LAST);
   // Server response will NOT match with autocomplete.
-  response.add_field()->set_overall_type_prediction(NAME_FIRST);
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(
+      NAME_FIRST);
   // Server response will have no data.
-  response.add_field()->set_overall_type_prediction(NO_SERVER_DATA);
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(
+      NO_SERVER_DATA);
   // Not logged.
-  response.add_field()->set_overall_type_prediction(NAME_MIDDLE);
+  form_suggestion->add_field_suggestions()->set_primary_type_prediction(
+      NAME_MIDDLE);
 
   std::string response_string;
   ASSERT_TRUE(response.SerializeToString(&response_string));
+  std::string encoded_response_string;
+  base::Base64Encode(response_string, &encoded_response_string);
 
   std::vector<std::string> signatures;
   signatures.push_back(form_structure_ptr->FormSignatureAsStr());
 
   base::HistogramTester histogram_tester;
-  autofill_manager_->OnLoadedServerPredictionsForTest(response_string,
+  autofill_manager_->OnLoadedServerPredictionsForTest(encoded_response_string,
                                                       signatures);
 
   // Verify that FormStructure::ParseQueryResponse was called (here and below).
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index 087bf69..83e9991 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -736,6 +736,17 @@
     field->set_type(control_type);
 }
 
+void FillQueryField(AutofillPageQueryRequest_Form_Field* field,
+                    unsigned signature,
+                    const char* name,
+                    const char* control_type) {
+  field->set_signature(signature);
+  if (name)
+    field->set_name(name);
+  if (control_type)
+    field->set_control_type(control_type);
+}
+
 void GenerateTestAutofillPopup(
     AutofillExternalDelegate* autofill_external_delegate) {
   int query_id = 1;
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index b5c2d002..900e51f 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -14,6 +14,7 @@
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/data_model/credit_card_cloud_token_data.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/proto/api_v1.pb.h"
 #include "components/autofill/core/browser/proto/server.pb.h"
 
 class PrefService;
@@ -268,6 +269,10 @@
                     unsigned signature,
                     const char* name,
                     const char* control_type);
+void FillQueryField(AutofillPageQueryRequest_Form_Field* field,
+                    unsigned signature,
+                    const char* name,
+                    const char* control_type);
 
 // Calls the required functions on the given external delegate to cause the
 // delegate to display a popup.
diff --git a/components/autofill/core/browser/proto/legacy_proto_bridge.cc b/components/autofill/core/browser/proto/legacy_proto_bridge.cc
index 9ce166c..2bb51d82 100644
--- a/components/autofill/core/browser/proto/legacy_proto_bridge.cc
+++ b/components/autofill/core/browser/proto/legacy_proto_bridge.cc
@@ -14,7 +14,8 @@
   api_field.set_signature(legacy_field.signature());
   api_field.set_name(legacy_field.name());
   api_field.set_control_type(legacy_field.type());
-  *api_field.mutable_metadata() = legacy_field.field_metadata();
+  if (legacy_field.has_field_metadata())
+    *api_field.mutable_metadata() = legacy_field.field_metadata();
   return api_field;
 }
 
@@ -22,7 +23,8 @@
     const AutofillQueryContents::Form& legacy_form) {
   AutofillPageQueryRequest::Form api_form;
   api_form.set_signature(legacy_form.signature());
-  *api_form.mutable_metadata() = legacy_form.form_metadata();
+  if (legacy_form.has_form_metadata())
+    *api_form.mutable_metadata() = legacy_form.form_metadata();
   for (const auto& legacy_field : legacy_form.field()) {
     *api_form.add_fields() = CreateLegacyFieldFromApiField(legacy_field);
   }
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 9fdaae0..2558915 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -197,7 +197,7 @@
 
 // Controls whether to use the API or use the legacy server.
 const base::Feature kAutofillUseApi{"AutofillUseApi",
-                                    base::FEATURE_DISABLED_BY_DEFAULT};
+                                    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Controls whether suggestions' labels use the improved label disambiguation
 // format.
diff --git a/components/autofill_assistant/browser/basic_interactions.cc b/components/autofill_assistant/browser/basic_interactions.cc
index fb9ee657..42655e2 100644
--- a/components/autofill_assistant/browser/basic_interactions.cc
+++ b/components/autofill_assistant/browser/basic_interactions.cc
@@ -8,6 +8,7 @@
 #include "base/i18n/time_formatting.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill_assistant/browser/script_executor_delegate.h"
 #include "components/autofill_assistant/browser/trigger_context.h"
 #include "components/autofill_assistant/browser/user_model.h"
@@ -144,6 +145,14 @@
           time, proto.date_format().date_format().c_str()));
       break;
     }
+    case ValueProto::kCreditCards:
+    case ValueProto::kProfiles:
+    case ValueProto::kLoginOptions:
+    case ValueProto::kCreditCardResponse:
+    case ValueProto::kLoginOptionResponse:
+      DVLOG(2) << "Error evaluating " << __func__ << ": kind not supported for "
+               << *value;
+      return false;
     case ValueProto::KIND_NOT_SET:
       DVLOG(2) << "Error evaluating " << __func__ << ": kind not set";
       return false;
@@ -259,6 +268,61 @@
   return true;
 }
 
+bool CreateCreditCardResponse(UserModel* user_model,
+                              const std::string& result_model_identifier,
+                              const CreateCreditCardResponseProto& proto) {
+  auto value = user_model->GetValue(proto.value());
+  if (!value.has_value()) {
+    DVLOG(2) << "Failed to find value in user model";
+    return false;
+  }
+
+  if (value->credit_cards().values().size() != 1) {
+    DVLOG(2) << "Error evaluating " << __func__
+             << ": expected single CreditCardProto, but got " << *value;
+    return false;
+  }
+
+  auto* credit_card =
+      user_model->GetCreditCard(value->credit_cards().values(0).guid());
+  if (!credit_card) {
+    DVLOG(2) << "Error evaluating " << __func__ << ": card not found for guid "
+             << value->credit_cards().values(0).guid();
+    return false;
+  }
+
+  // The result is intentionally not client_side_only, irrespective of input.
+  ValueProto result;
+  result.mutable_credit_card_response()->set_network(
+      autofill::data_util::GetPaymentRequestData(credit_card->network())
+          .basic_card_issuer_network);
+  user_model->SetValue(result_model_identifier, result);
+  return true;
+}
+
+bool CreateLoginOptionResponse(UserModel* user_model,
+                               const std::string& result_model_identifier,
+                               const CreateLoginOptionResponseProto& proto) {
+  auto value = user_model->GetValue(proto.value());
+  if (!value.has_value()) {
+    DVLOG(2) << "Failed to find value in user model";
+    return false;
+  }
+
+  if (value->login_options().values().size() != 1) {
+    DVLOG(2) << "Error evaluating " << __func__
+             << ": expected single LoginOptionProto, but got " << *value;
+    return false;
+  }
+
+  // The result is intentionally not client_side_only, irrespective of input.
+  ValueProto result;
+  result.mutable_login_option_response()->set_payload(
+      value->login_options().values(0).payload());
+  user_model->SetValue(result_model_identifier, result);
+  return true;
+}
+
 }  // namespace
 
 base::WeakPtr<BasicInteractions> BasicInteractions::GetWeakPtr() {
@@ -334,6 +398,25 @@
       }
       return IntegerSum(delegate_->GetUserModel(),
                         proto.result_model_identifier(), proto.integer_sum());
+    case ComputeValueProto::kCreateCreditCardResponse:
+      if (!proto.create_credit_card_response().has_value()) {
+        DVLOG(2) << "Error computing ComputeValue::CreateCreditCardResponse: "
+                    "no value specified";
+        return false;
+      }
+      return CreateCreditCardResponse(delegate_->GetUserModel(),
+                                      proto.result_model_identifier(),
+                                      proto.create_credit_card_response());
+    case ComputeValueProto::kCreateLoginOptionResponse:
+      if (!proto.create_login_option_response().has_value()) {
+        DVLOG(2) << "Error computing ComputeValue::CreateLoginOptionResponse: "
+                    "no value specified";
+        return false;
+      }
+      return CreateLoginOptionResponse(delegate_->GetUserModel(),
+                                       proto.result_model_identifier(),
+                                       proto.create_login_option_response());
+      break;
     case ComputeValueProto::KIND_NOT_SET:
       DVLOG(2) << "Error computing value: kind not set";
       return false;
diff --git a/components/autofill_assistant/browser/basic_interactions_unittest.cc b/components/autofill_assistant/browser/basic_interactions_unittest.cc
index f7762ad..2c83b02 100644
--- a/components/autofill_assistant/browser/basic_interactions_unittest.cc
+++ b/components/autofill_assistant/browser/basic_interactions_unittest.cc
@@ -3,9 +3,11 @@
 // found in the LICENSE file.
 
 #include "components/autofill_assistant/browser/basic_interactions.h"
+#include "base/guid.h"
 #include "base/test/gmock_callback_support.h"
 #include "base/test/icu_test_util.h"
 #include "base/test/mock_callback.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill_assistant/browser/fake_script_executor_delegate.h"
 #include "components/autofill_assistant/browser/generic_ui.pb.h"
 #include "components/autofill_assistant/browser/user_model.h"
@@ -395,6 +397,84 @@
   EXPECT_EQ(user_model_.GetValue("result"), SimpleValue(true, true));
 }
 
+TEST_F(BasicInteractionsTest, ComputeValueCreateCreditCardResponse) {
+  ComputeValueProto proto;
+  proto.mutable_create_credit_card_response();
+
+  // Missing fields.
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+  proto.mutable_create_credit_card_response()
+      ->mutable_value()
+      ->set_model_identifier("value");
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+  proto.set_result_model_identifier("result");
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+
+  autofill::CreditCard credit_card(base::GenerateGUID(),
+                                   "https://www.example.com");
+  autofill::test::SetCreditCardInfo(&credit_card, "Marion Mitchell",
+                                    "4111 1111 1111 1111", "01", "2050", "");
+  user_model_.AddCreditCard(
+      std::make_unique<autofill::CreditCard>(credit_card));
+
+  ValueProto value_wrong_guid;
+  value_wrong_guid.mutable_credit_cards()->add_values()->set_guid("wrong");
+  user_model_.SetValue("value", value_wrong_guid);
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+
+  ValueProto value_correct_guid;
+  value_correct_guid.mutable_credit_cards()->add_values()->set_guid(
+      credit_card.guid());
+  user_model_.SetValue("value", value_correct_guid);
+  EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
+  ValueProto expected_response_value;
+  expected_response_value.mutable_credit_card_response()->set_network("visa");
+  expected_response_value.set_is_client_side_only(false);
+  EXPECT_EQ(user_model_.GetValue("result"), expected_response_value);
+
+  // CreateCreditCardResponse is allowed to extract the card network from
+  // client-only values.
+  value_correct_guid.set_is_client_side_only(true);
+  user_model_.SetValue("value", value_correct_guid);
+  EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
+  EXPECT_EQ(user_model_.GetValue("result"), expected_response_value);
+
+  // Size != 1.
+  ValueProto value;
+  value.mutable_credit_cards()->add_values()->set_guid(credit_card.guid());
+  value.mutable_credit_cards()->add_values()->set_guid(credit_card.guid());
+  user_model_.SetValue("value", value);
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+}
+
+TEST_F(BasicInteractionsTest, ComputeValueCreateLoginOptionResponse) {
+  ComputeValueProto proto;
+  proto.mutable_create_login_option_response();
+
+  // Missing fields.
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+  proto.mutable_create_login_option_response()
+      ->mutable_value()
+      ->set_model_identifier("value");
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+  proto.set_result_model_identifier("result");
+  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+
+  ValueProto value;
+  value.mutable_login_options()->add_values()->set_payload("payload");
+  value.set_is_client_side_only(true);
+  user_model_.SetValue("value", value);
+  EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
+
+  // LoginOptionResponseProto is allowed to extract the payload from
+  // client-only values.
+  ValueProto expected_response_value;
+  expected_response_value.mutable_login_option_response()->set_payload(
+      "payload");
+  expected_response_value.set_is_client_side_only(false);
+  EXPECT_EQ(user_model_.GetValue("result"), expected_response_value);
+}
+
 TEST_F(BasicInteractionsTest, ToggleUserAction) {
   ToggleUserActionProto proto;
   ValueProto user_actions_value;
diff --git a/components/autofill_assistant/browser/generic_ui.proto b/components/autofill_assistant/browser/generic_ui.proto
index f6f3aac..5e136197 100644
--- a/components/autofill_assistant/browser/generic_ui.proto
+++ b/components/autofill_assistant/browser/generic_ui.proto
@@ -120,6 +120,10 @@
     ValueComparisonProto comparison = 6;
     // Computes the sum of two single integers.
     IntegerSumProto integer_sum = 7;
+    // Writes safe-to-transmit parts of the input credit card to the result.
+    CreateCreditCardResponseProto create_credit_card_response = 8;
+    // Writes safe-to-transmit parts of the input login option to the result.
+    CreateLoginOptionResponseProto create_login_option_response = 9;
   }
 
   // The model identifier to write the result to.
@@ -194,6 +198,18 @@
   reserved 1, 2;
 }
 
+// Creates a safe-to-transmit CreditCardResponseProto for the input |value|.
+message CreateCreditCardResponseProto {
+  // The input CreditCardProto.
+  optional ValueReferenceProto value = 1;
+}
+
+// Creates a safe-to-transmit LoginOptionResponseProto for the input |value|.
+message CreateLoginOptionResponseProto {
+  // The input LoginOptionProto.
+  optional ValueReferenceProto value = 1;
+}
+
 // Displays a standard info popup.
 message ShowInfoPopupProto {
   optional InfoPopupProto info_popup = 1;
diff --git a/components/autofill_assistant/browser/model.proto b/components/autofill_assistant/browser/model.proto
index a6a1433..607825b 100644
--- a/components/autofill_assistant/browser/model.proto
+++ b/components/autofill_assistant/browser/model.proto
@@ -15,6 +15,7 @@
     optional string identifier = 1;
     optional ValueProto value = 2;
   }
+
   // Maps model identifiers to their values.
   repeated ModelValue values = 1;
 }
@@ -28,6 +29,11 @@
     IntList ints = 3;
     UserActionList user_actions = 4;
     DateList dates = 5;
+    CreditCardList credit_cards = 7;
+    ProfileList profiles = 8;
+    LoginOptionList login_options = 9;
+    CreditCardResponseProto credit_card_response = 10;
+    LoginOptionResponseProto login_option_response = 11;
   }
   // If set to true, this value will not be sent to the backend. This flag is
   // mostly used internally, to prevent certain values from leaving the device.
@@ -63,6 +69,18 @@
   repeated DateProto values = 1;
 }
 
+message CreditCardList {
+  repeated AutofillCreditCardProto values = 1;
+}
+
+message ProfileList {
+  repeated AutofillProfileProto values = 1;
+}
+
+message LoginOptionList {
+  repeated LoginOptionProto values = 1;
+}
+
 // Next: 25
 enum ProcessedActionStatusProto {
   UNKNOWN_ACTION_STATUS = 0;
@@ -299,3 +317,27 @@
   optional DateProto date = 1;
   optional TimeProto time = 2;
 }
+
+message AutofillCreditCardProto {
+  // The GUID of the card.
+  optional string guid = 1;
+}
+
+message CreditCardResponseProto {
+  optional string network = 1;
+}
+
+message AutofillProfileProto {
+  // The GUID of the profile.
+  optional string guid = 1;
+}
+
+message LoginOptionProto {
+  optional string label = 1;
+  optional string sublabel = 2;
+  optional bytes payload = 3;
+}
+
+message LoginOptionResponseProto {
+  optional bytes payload = 1;
+}
diff --git a/components/autofill_assistant/browser/user_model.cc b/components/autofill_assistant/browser/user_model.cc
index 26986d5..9520b83 100644
--- a/components/autofill_assistant/browser/user_model.cc
+++ b/components/autofill_assistant/browser/user_model.cc
@@ -143,4 +143,37 @@
   observers_.RemoveObserver(observer);
 }
 
+void UserModel::AddCreditCard(
+    std::unique_ptr<autofill::CreditCard> credit_card) {
+  if (!credit_card) {
+    return;
+  }
+  credit_cards_[credit_card->guid()] = std::move(credit_card);
+}
+
+void UserModel::AddProfile(std::unique_ptr<autofill::AutofillProfile> profile) {
+  if (!profile) {
+    return;
+  }
+  profiles_[profile->guid()] = std::move(profile);
+}
+
+const autofill::CreditCard* UserModel::GetCreditCard(
+    const std::string& guid) const {
+  auto it = credit_cards_.find(guid);
+  if (it == credit_cards_.end()) {
+    return nullptr;
+  }
+  return it->second.get();
+}
+
+const autofill::AutofillProfile* UserModel::GetProfile(
+    const std::string& guid) const {
+  auto it = profiles_.find(guid);
+  if (it == profiles_.end()) {
+    return nullptr;
+  }
+  return it->second.get();
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/user_model.h b/components/autofill_assistant/browser/user_model.h
index 520b907d..dc0dba8 100644
--- a/components/autofill_assistant/browser/user_model.h
+++ b/components/autofill_assistant/browser/user_model.h
@@ -13,9 +13,10 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/optional.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill_assistant/browser/model.pb.h"
 #include "components/autofill_assistant/browser/value_util.h"
-
 namespace autofill_assistant {
 
 // Manages a map of |ValueProto| instances and notifies observers of changes.
@@ -71,6 +72,18 @@
     return values;
   }
 
+  // Adds |credit_card| to the set of available cards.
+  void AddCreditCard(std::unique_ptr<autofill::CreditCard> credit_card);
+
+  // Adds |profile| to the set of available cards.
+  void AddProfile(std::unique_ptr<autofill::AutofillProfile> profile);
+
+  // Returns the credit card with |guid| or nullptr if there is no such card.
+  const autofill::CreditCard* GetCreditCard(const std::string& guid) const;
+
+  // Returns the profile with |guid| or nullptr if there is no such profile.
+  const autofill::AutofillProfile* GetProfile(const std::string& guid) const;
+
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
@@ -88,6 +101,8 @@
   friend class UserModelTest;
 
   std::map<std::string, ValueProto> values_;
+  std::map<std::string, std::unique_ptr<autofill::CreditCard>> credit_cards_;
+  std::map<std::string, std::unique_ptr<autofill::AutofillProfile>> profiles_;
   base::ObserverList<Observer> observers_;
   base::WeakPtrFactory<UserModel> weak_ptr_factory_{this};
   DISALLOW_COPY_AND_ASSIGN(UserModel);
diff --git a/components/autofill_assistant/browser/value_util.cc b/components/autofill_assistant/browser/value_util.cc
index 3fd5298..8511690 100644
--- a/components/autofill_assistant/browser/value_util.cc
+++ b/components/autofill_assistant/browser/value_util.cc
@@ -54,6 +54,17 @@
       return value_a.user_actions().values() == value_b.user_actions().values();
     case ValueProto::kDates:
       return value_a.dates().values() == value_b.dates().values();
+    case ValueProto::kCreditCards:
+      return value_a.credit_cards().values() == value_b.credit_cards().values();
+    case ValueProto::kProfiles:
+      return value_a.profiles().values() == value_b.profiles().values();
+    case ValueProto::kLoginOptions:
+      return value_a.login_options().values() ==
+             value_b.login_options().values();
+    case ValueProto::kCreditCardResponse:
+      return value_a.credit_card_response() == value_b.credit_card_response();
+    case ValueProto::kLoginOptionResponse:
+      return value_a.login_option_response() == value_b.login_option_response();
     case ValueProto::KIND_NOT_SET:
       return true;
   }
@@ -83,6 +94,11 @@
       return value_a.dates().values(0) < value_b.dates().values(0);
     case ValueProto::kUserActions:
     case ValueProto::kBooleans:
+    case ValueProto::kCreditCards:
+    case ValueProto::kProfiles:
+    case ValueProto::kLoginOptions:
+    case ValueProto::kCreditCardResponse:
+    case ValueProto::kLoginOptionResponse:
     case ValueProto::KIND_NOT_SET:
       NOTREACHED();
       return false;
@@ -143,6 +159,33 @@
   return tuple_a < tuple_b;
 }
 
+bool operator==(const AutofillCreditCardProto& value_a,
+                const AutofillCreditCardProto& value_b) {
+  return value_a.guid() == value_b.guid();
+}
+
+bool operator==(const AutofillProfileProto& value_a,
+                const AutofillProfileProto& value_b) {
+  return value_a.guid() == value_b.guid();
+}
+
+bool operator==(const LoginOptionProto& value_a,
+                const LoginOptionProto& value_b) {
+  return value_a.label() == value_b.label() &&
+         value_a.sublabel() == value_b.sublabel() &&
+         value_a.payload() == value_b.payload();
+}
+
+bool operator==(const CreditCardResponseProto& value_a,
+                const CreditCardResponseProto& value_b) {
+  return value_a.network() == value_b.network();
+}
+
+bool operator==(const LoginOptionResponseProto& value_a,
+                const LoginOptionResponseProto& value_b) {
+  return value_a.payload() == value_b.payload();
+}
+
 // Intended for debugging. Writes a string representation of |values| to |out|.
 template <typename T>
 std::ostream& WriteRepeatedField(std::ostream& out, const T& values) {
@@ -170,18 +213,44 @@
   return WriteRepeatedField(out, values);
 }
 
-// Intended for debugging. '<<' operator specialization for UserActionProto.
 std::ostream& operator<<(std::ostream& out, const UserActionProto& value) {
   out << value.identifier();
   return out;
 }
 
-// Intended for debugging. '<<' operator specialization for DateProto.
 std::ostream& operator<<(std::ostream& out, const DateProto& value) {
   out << value.year() << "-" << value.month() << "-" << value.day();
   return out;
 }
 
+std::ostream& operator<<(std::ostream& out,
+                         const AutofillCreditCardProto& value) {
+  out << value.guid();
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const AutofillProfileProto& value) {
+  out << value.guid();
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const LoginOptionProto& value) {
+  out << value.label() << ", " << value.sublabel() << ", " << value.payload();
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         const CreditCardResponseProto& value) {
+  out << value.network();
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         const LoginOptionResponseProto& value) {
+  out << value.payload();
+  return out;
+}
+
 // Intended for debugging.  Writes a string representation of |value| to |out|.
 std::ostream& operator<<(std::ostream& out, const ValueProto& value) {
   switch (value.kind_case()) {
@@ -200,6 +269,22 @@
     case ValueProto::kDates:
       out << value.dates().values();
       break;
+    case ValueProto::kCreditCards:
+      out << value.credit_cards().values();
+      break;
+    case ValueProto::kProfiles:
+      out << value.profiles().values();
+      break;
+    case ValueProto::kLoginOptions:
+      out << value.login_options().values();
+      break;
+    case ValueProto::kCreditCardResponse:
+      out << value.credit_card_response();
+      break;
+    case ValueProto::kLoginOptionResponse:
+      out << value.login_option_response();
+      ;
+      break;
     case ValueProto::KIND_NOT_SET:
       break;
   }
@@ -310,15 +395,25 @@
 int GetValueSize(const ValueProto& value) {
   switch (value.kind_case()) {
     case ValueProto::kStrings:
-      return value.strings().values_size();
+      return value.strings().values().size();
     case ValueProto::kBooleans:
-      return value.booleans().values_size();
+      return value.booleans().values().size();
     case ValueProto::kInts:
-      return value.ints().values_size();
+      return value.ints().values().size();
     case ValueProto::kUserActions:
-      return value.user_actions().values_size();
+      return value.user_actions().values().size();
     case ValueProto::kDates:
-      return value.dates().values_size();
+      return value.dates().values().size();
+    case ValueProto::kCreditCards:
+      return value.credit_cards().values().size();
+    case ValueProto::kProfiles:
+      return value.profiles().values().size();
+    case ValueProto::kLoginOptions:
+      return value.login_options().values().size();
+    case ValueProto::kCreditCardResponse:
+      return 1;
+    case ValueProto::kLoginOptionResponse:
+      return 1;
     case ValueProto::KIND_NOT_SET:
       return 0;
   }
@@ -354,6 +449,26 @@
       *nth_value.mutable_dates()->add_values() =
           value.dates().values().at(index);
       return nth_value;
+    case ValueProto::kCreditCards:
+      *nth_value.mutable_credit_cards()->add_values() =
+          value.credit_cards().values().at(index);
+      return nth_value;
+    case ValueProto::kProfiles:
+      *nth_value.mutable_profiles()->add_values() =
+          value.profiles().values().at(index);
+      return nth_value;
+    case ValueProto::kLoginOptions:
+      *nth_value.mutable_login_options()->add_values() =
+          value.login_options().values().at(index);
+      return nth_value;
+    case ValueProto::kCreditCardResponse:
+      DCHECK(index == 0);
+      nth_value = value;
+      return nth_value;
+    case ValueProto::kLoginOptionResponse:
+      DCHECK(index == 0);
+      nth_value = value;
+      return nth_value;
     case ValueProto::KIND_NOT_SET:
       return base::nullopt;
   }
diff --git a/components/autofill_assistant/browser/value_util.h b/components/autofill_assistant/browser/value_util.h
index 5137ceb5..9d44a6c 100644
--- a/components/autofill_assistant/browser/value_util.h
+++ b/components/autofill_assistant/browser/value_util.h
@@ -38,6 +38,26 @@
 bool operator==(const DateProto& value_a, const DateProto& value_b);
 bool operator<(const DateProto& value_a, const DateProto& value_b);
 
+// Custom comparison operator for |AutofillCreditCardProto|.
+bool operator==(const AutofillCreditCardProto& value_a,
+                const AutofillCreditCardProto& value_b);
+
+// Custom comparison operator for |AutofillProfileProto|.
+bool operator==(const AutofillProfileProto& value_a,
+                const AutofillProfileProto& value_b);
+
+// Custom comparison operator for |LoginOptionProto|.
+bool operator==(const LoginOptionProto& value_a,
+                const LoginOptionProto& value_b);
+
+// Custom comparison operator for |CreditCardResponseProto|.
+bool operator==(const CreditCardResponseProto& value_a,
+                const CreditCardResponseProto& value_b);
+
+// Custom comparison operator for |LoginOptionResponseProto|.
+bool operator==(const LoginOptionResponseProto& value_a,
+                const LoginOptionResponseProto& value_b);
+
 // Intended for debugging.
 std::ostream& operator<<(std::ostream& out, const ValueProto& value);
 std::ostream& operator<<(std::ostream& out,
@@ -46,6 +66,14 @@
                          const ModelProto::ModelValue& value);
 std::ostream& operator<<(std::ostream& out, const UserActionProto& value);
 std::ostream& operator<<(std::ostream& out, const DateProto& value);
+std::ostream& operator<<(std::ostream& out,
+                         const AutofillCreditCardProto& value);
+std::ostream& operator<<(std::ostream& out, const AutofillProfileProto& value);
+std::ostream& operator<<(std::ostream& out, const LoginOptionProto& value);
+std::ostream& operator<<(std::ostream& out,
+                         const CreditCardResponseProto& value);
+std::ostream& operator<<(std::ostream& out,
+                         const LoginOptionResponseProto& value);
 
 // Convenience constructors.
 ValueProto SimpleValue(bool value, bool is_client_side_only = false);
diff --git a/components/autofill_assistant/browser/value_util_unittest.cc b/components/autofill_assistant/browser/value_util_unittest.cc
index 7cf4d2f..b33a2d2 100644
--- a/components/autofill_assistant/browser/value_util_unittest.cc
+++ b/components/autofill_assistant/browser/value_util_unittest.cc
@@ -171,6 +171,67 @@
   EXPECT_FALSE(value_a == value_b);
 }
 
+TEST_F(ValueUtilTest, CreditCardComparison) {
+  ValueProto value_a;
+  value_a.mutable_credit_cards()->add_values()->set_guid("GUID");
+  ValueProto value_b = value_a;
+  EXPECT_TRUE(value_a == value_b);
+
+  value_b.mutable_credit_cards()->mutable_values(0)->set_guid("wrong");
+  EXPECT_FALSE(value_a == value_b);
+}
+
+TEST_F(ValueUtilTest, ProfileComparison) {
+  ValueProto value_a;
+  value_a.mutable_profiles()->add_values()->set_guid("GUID");
+  ValueProto value_b = value_a;
+  EXPECT_TRUE(value_a == value_b);
+
+  value_b.mutable_profiles()->mutable_values(0)->set_guid("wrong");
+  EXPECT_FALSE(value_a == value_b);
+}
+
+TEST_F(ValueUtilTest, LoginOptionComparison) {
+  ValueProto value_a;
+  auto* option_a = value_a.mutable_login_options()->add_values();
+  option_a->set_label("label");
+  option_a->set_sublabel("sublabel");
+  option_a->set_payload("payload");
+  ValueProto value_b = value_a;
+  EXPECT_TRUE(value_a == value_b);
+
+  option_a->set_label("different");
+  EXPECT_FALSE(value_a == value_b);
+
+  option_a->set_label("label");
+  option_a->set_sublabel("different");
+  EXPECT_FALSE(value_a == value_b);
+
+  option_a->set_sublabel("sublabel");
+  option_a->set_payload("different");
+  EXPECT_FALSE(value_a == value_b);
+}
+
+TEST_F(ValueUtilTest, CreditCardResponseComparison) {
+  ValueProto value_a;
+  value_a.mutable_credit_card_response()->set_network("network");
+  ValueProto value_b = value_a;
+  EXPECT_TRUE(value_a == value_b);
+
+  value_b.mutable_credit_card_response()->set_network("different");
+  EXPECT_FALSE(value_a == value_b);
+}
+
+TEST_F(ValueUtilTest, LoginOptionResponseComparison) {
+  ValueProto value_a;
+  value_a.mutable_login_option_response()->set_payload("payload");
+  ValueProto value_b = value_a;
+  EXPECT_TRUE(value_a == value_b);
+
+  value_b.mutable_login_option_response()->set_payload("different");
+  EXPECT_FALSE(value_a == value_b);
+}
+
 TEST_F(ValueUtilTest, AreAllValuesOfType) {
   ValueProto value_a;
   ValueProto value_b;
diff --git a/components/browser_ui/settings/android/BUILD.gn b/components/browser_ui/settings/android/BUILD.gn
index 3bc0f42..d8e1bf4 100644
--- a/components/browser_ui/settings/android/BUILD.gn
+++ b/components/browser_ui/settings/android/BUILD.gn
@@ -25,10 +25,6 @@
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_preference_preference_java",
-
-    # TODO(crbug.com/1017190): Remove the following deps once we stop linting individual targets.
-    "//components/browser_ui/styles/android:java_resources",
-    "//third_party/android_deps:com_google_android_material_material_java",
     "//ui/android:ui_java",
   ]
 }
diff --git a/components/browser_ui/styles/android/BUILD.gn b/components/browser_ui/styles/android/BUILD.gn
index e3aa975..df18109 100644
--- a/components/browser_ui/styles/android/BUILD.gn
+++ b/components/browser_ui/styles/android/BUILD.gn
@@ -11,9 +11,6 @@
     ":java_resources",
     "//base:base_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
-
-    # TODO(crbug.com/1017190): Remove this workaround.
-    "//ui/android:ui_java",
   ]
 }
 
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
index 2d46434..1c780c86 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
@@ -97,12 +97,12 @@
     // An extra that may be specified on an intent:// URL that contains an encoded value for the
     // referrer field passed to the market:// URL in the case where the app is not present.
     @VisibleForTesting
-    public static final String EXTRA_MARKET_REFERRER = "market_referrer";
+    static final String EXTRA_MARKET_REFERRER = "market_referrer";
 
     // A mask of flags that are safe for untrusted content to use when starting an Activity.
     // This list is not exhaustive and flags not listed here are not necessarily unsafe.
     @VisibleForTesting
-    public static final int ALLOWED_INTENT_FLAGS = Intent.FLAG_EXCLUDE_STOPPED_PACKAGES
+    static final int ALLOWED_INTENT_FLAGS = Intent.FLAG_EXCLUDE_STOPPED_PACKAGES
             | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP
             | Intent.FLAG_ACTIVITY_MATCH_EXTERNAL | Intent.FLAG_ACTIVITY_NEW_TASK
             | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
@@ -111,7 +111,7 @@
     // These values are persisted in histograms. Please do not renumber. Append only.
     @IntDef({AiaIntent.FALLBACK_USED, AiaIntent.SERP, AiaIntent.OTHER})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface AiaIntent {
+    private @interface AiaIntent {
         int FALLBACK_USED = 0;
         int SERP = 1;
         int OTHER = 2;
@@ -131,7 +131,7 @@
             StandardActions.WEB_SEARCH, StandardActions.FACTORY_TEST, StandardActions.OTHER})
     @Retention(RetentionPolicy.SOURCE)
     @VisibleForTesting
-    public @interface StandardActions {
+    @interface StandardActions {
         int MAIN = 0;
         int VIEW = 1;
         int ATTACH_DATA = 2;
@@ -158,8 +158,7 @@
     }
 
     @VisibleForTesting
-    public static final String INTENT_ACTION_HISTOGRAM =
-            "Android.Intent.OverrideUrlLoadingIntentAction";
+    static final String INTENT_ACTION_HISTOGRAM = "Android.Intent.OverrideUrlLoadingIntentAction";
 
     private final ExternalNavigationDelegate mDelegate;
 
@@ -366,6 +365,7 @@
      * @param referrerUrl The HTTP referrer URL.
      * @param needsToCloseTab Whether this action should close the current tab.
      */
+    @VisibleForTesting
     protected void startFileIntent(
             final Intent intent, final String referrerUrl, final boolean needsToCloseTab) {
         PermissionCallback permissionCallback = new PermissionCallback() {
@@ -399,6 +399,7 @@
      * @return OverrideUrlLoadingResult (if the tab has been clobbered, or we're launching an
      *         intent.)
      */
+    @VisibleForTesting
     protected @OverrideUrlLoadingResult int clobberCurrentTab(String url, String referrerUrl) {
         int transitionType = PageTransition.LINK;
         final LoadUrlParams loadUrlParams = new LoadUrlParams(url, transitionType);
@@ -526,7 +527,7 @@
 
     /** Wrapper of check against the feature to support overriding for testing. */
     @VisibleForTesting
-    public boolean blockExternalFormRedirectsWithoutGesture() {
+    boolean blockExternalFormRedirectsWithoutGesture() {
         return ExternalIntentsFeatureList.isEnabled(
                 ExternalIntentsFeatureList.INTENT_BLOCK_EXTERNAL_FORM_REDIRECT_NO_GESTURE);
     }
@@ -1208,7 +1209,8 @@
     /**
      * @return Whether the |url| could be handled by an external application on the system.
      */
-    public boolean canExternalAppHandleUrl(String url) {
+    @VisibleForTesting
+    boolean canExternalAppHandleUrl(String url) {
         if (url.startsWith(WTAI_MC_URL_PREFIX)) return true;
         Intent intent;
         try {
@@ -1291,7 +1293,7 @@
      * @return Whether the URL is a file download.
      */
     @VisibleForTesting
-    public boolean isPdfDownload(String url) {
+    boolean isPdfDownload(String url) {
         String fileExtension = MimeTypeMap.getFileExtensionFromUrl(url);
         if (TextUtils.isEmpty(fileExtension)) return false;
 
@@ -1308,7 +1310,7 @@
     /**
      * Records the dispatching of an external intent.
      */
-    static void recordExternalNavigationDispatched(Intent intent) {
+    private static void recordExternalNavigationDispatched(Intent intent) {
         ArrayList<String> specializedHandlers =
                 intent.getStringArrayListExtra(EXTRA_EXTERNAL_NAV_PACKAGES);
         if (specializedHandlers != null && specializedHandlers.size() > 0) {
@@ -1323,7 +1325,7 @@
      *
      * @param intent Intent to open.
      */
-    static void forcePdfViewerAsIntentHandlerIfNeeded(Intent intent) {
+    private static void forcePdfViewerAsIntentHandlerIfNeeded(Intent intent) {
         if (intent == null || !isPdfIntent(intent)) return;
         resolveIntent(intent, true /* allowSelfOpen (ignored) */);
     }
@@ -1481,7 +1483,7 @@
      * Returns the number of specialized intent handlers in {@params infos}. Specialized intent
      * handlers are intent handlers which handle only a few URLs (e.g. google maps or youtube).
      */
-    int countSpecializedHandlers(List<ResolveInfo> infos) {
+    private int countSpecializedHandlers(List<ResolveInfo> infos) {
         return getSpecializedHandlersWithFilter(
                 infos, null, mDelegate.handlesInstantAppLaunchingInternally())
                 .size();
@@ -1490,12 +1492,13 @@
     /**
      * Returns the subset of {@params infos} that are specialized intent handlers.
      */
-    ArrayList<String> getSpecializedHandlers(List<ResolveInfo> infos) {
+    private ArrayList<String> getSpecializedHandlers(List<ResolveInfo> infos) {
         return getSpecializedHandlersWithFilter(
                 infos, null, mDelegate.handlesInstantAppLaunchingInternally());
     }
 
-    static boolean matchResolveInfoExceptWildCardHost(ResolveInfo info, String filterPackageName) {
+    private static boolean matchResolveInfoExceptWildCardHost(
+            ResolveInfo info, String filterPackageName) {
         IntentFilter intentFilter = info.filter;
         if (intentFilter == null) {
             // Error on the side of classifying ResolveInfo as generic.
@@ -1525,7 +1528,6 @@
         return true;
     }
 
-    @VisibleForTesting
     public static ArrayList<String> getSpecializedHandlersWithFilter(List<ResolveInfo> infos,
             String filterPackageName, boolean handlesInstantAppLaunchingInternally) {
         ArrayList<String> result = new ArrayList<>();
@@ -1590,6 +1592,7 @@
      * @param url The requested url.
      * @return Whether we should block the navigation and request file access before proceeding.
      */
+    @VisibleForTesting
     protected boolean shouldRequestFileAccess(String url) {
         // If the tab is null, then do not attempt to prompt for access.
         if (!mDelegate.hasValidTab()) return false;
@@ -1624,6 +1627,7 @@
     /**
      * @return whether this navigation is from the search results page.
      */
+    @VisibleForTesting
     protected boolean isSerpReferrer() {
         String referrerUrl = getReferrerUrl();
         if (referrerUrl == null) return false;
diff --git a/components/feed/core/proto/libraries/api/internal/stream_data.proto b/components/feed/core/proto/libraries/api/internal/stream_data.proto
index e66a0b8..f5e0b26 100644
--- a/components/feed/core/proto/libraries/api/internal/stream_data.proto
+++ b/components/feed/core/proto/libraries/api/internal/stream_data.proto
@@ -229,6 +229,10 @@
 
   optional feedwire1.ActionPayload payload = 6;
 
+  // The duration for the action in milliseconds. In case of view actions this
+  // is the duration for which the content is considered "viewed".
+  optional int64 duration_ms = 7;
+
   reserved 1, 5;  // deprecated fields
 }
 
diff --git a/components/lookalikes/core/lookalike_url_util.cc b/components/lookalikes/core/lookalike_url_util.cc
index a21175fc..1f081d0 100644
--- a/components/lookalikes/core/lookalike_url_util.cc
+++ b/components/lookalikes/core/lookalike_url_util.cc
@@ -332,8 +332,7 @@
           lookalikes::features::kDetectTargetEmbeddingLookalikes)) {
     return true;
   }
-  return match_type == LookalikeUrlMatchType::kTopSite &&
-         navigated_domain.idn_result.matching_top_domain.is_top_500;
+  return match_type == LookalikeUrlMatchType::kSkeletonMatchTop500;
 }
 
 bool GetMatchingDomain(
@@ -367,7 +366,9 @@
       DCHECK_NE(navigated_domain.domain_and_registry,
                 navigated_domain.idn_result.matching_top_domain.domain);
       *matched_domain = navigated_domain.idn_result.matching_top_domain.domain;
-      *match_type = LookalikeUrlMatchType::kTopSite;
+      *match_type = navigated_domain.idn_result.matching_top_domain.is_top_500
+                        ? LookalikeUrlMatchType::kSkeletonMatchTop500
+                        : LookalikeUrlMatchType::kSkeletonMatchTop5k;
       return true;
     }
   }
@@ -407,9 +408,6 @@
 
 void RecordUMAFromMatchType(LookalikeUrlMatchType match_type) {
   switch (match_type) {
-    case LookalikeUrlMatchType::kTopSite:
-      RecordEvent(NavigationSuggestionEvent::kMatchTopSite);
-      break;
     case LookalikeUrlMatchType::kSiteEngagement:
       RecordEvent(NavigationSuggestionEvent::kMatchSiteEngagement);
       break;
@@ -422,6 +420,12 @@
     case LookalikeUrlMatchType::kTargetEmbedding:
       RecordEvent(NavigationSuggestionEvent::kMatchTargetEmbedding);
       break;
+    case LookalikeUrlMatchType::kSkeletonMatchTop500:
+      RecordEvent(NavigationSuggestionEvent::kMatchSkeletonTop500);
+      break;
+    case LookalikeUrlMatchType::kSkeletonMatchTop5k:
+      RecordEvent(NavigationSuggestionEvent::kMatchSkeletonTop5k);
+      break;
     case LookalikeUrlMatchType::kNone:
       break;
   }
diff --git a/components/lookalikes/core/lookalike_url_util.h b/components/lookalikes/core/lookalike_url_util.h
index 2ab396e0..1bc49c2 100644
--- a/components/lookalikes/core/lookalike_url_util.h
+++ b/components/lookalikes/core/lookalike_url_util.h
@@ -25,15 +25,18 @@
 // Used for UKM. There is only a single LookalikeUrlMatchType per navigation.
 enum class LookalikeUrlMatchType {
   kNone = 0,
-  kTopSite = 1,
+  // DEPRECATED: Use kSkeletonMatchTop500 or kSkeletonMatchTop5k.
+  // kTopSite = 1,
   kSiteEngagement = 2,
   kEditDistance = 3,
   kEditDistanceSiteEngagement = 4,
   kTargetEmbedding = 5,
+  kSkeletonMatchTop500 = 6,
+  kSkeletonMatchTop5k = 7,
 
   // Append new items to the end of the list above; do not modify or replace
   // existing values. Comment out obsolete items.
-  kMaxValue = kTargetEmbedding,
+  kMaxValue = kSkeletonMatchTop5k,
 };
 
 // Used for UKM. There is only a single LookalikeUrlBlockingPageUserAction per
@@ -55,15 +58,18 @@
   // Interstitial results recorded using security_interstitials::MetricsHelper
   // kInfobarShown = 1,
   // kLinkClicked = 2,
-  kMatchTopSite = 3,
+  // DEPRECATED: Use kMatchSkeletonTop500 or kMatchSkeletonTop5k.
+  // kMatchTopSite = 3,
   kMatchSiteEngagement = 4,
   kMatchEditDistance = 5,
   kMatchEditDistanceSiteEngagement = 6,
   kMatchTargetEmbedding = 7,
+  kMatchSkeletonTop500 = 8,
+  kMatchSkeletonTop5k = 9,
 
   // Append new items to the end of the list above; do not modify or
   // replace existing values. Comment out obsolete items.
-  kMaxValue = kMatchTargetEmbedding,
+  kMaxValue = kMatchSkeletonTop5k,
 };
 
 struct DomainInfo {
diff --git a/components/page_info/android/BUILD.gn b/components/page_info/android/BUILD.gn
index ee73a9c..060e48f 100644
--- a/components/page_info/android/BUILD.gn
+++ b/components/page_info/android/BUILD.gn
@@ -10,10 +10,13 @@
     "certificate_viewer_android.cc",
     "connection_info_popup_android.cc",
     "connection_info_popup_android.h",
+    "features.cc",
+    "features.h",
     "page_info_client.cc",
     "page_info_client.h",
     "page_info_controller_android.cc",
     "page_info_controller_android.h",
+    "page_info_feature_list.cc",
   ]
   deps = [
     "//base",
@@ -50,6 +53,8 @@
     "java/res/layout/cookie_controls_view.xml",
     "java/res/layout/page_info.xml",
     "java/res/layout/page_info_permission_row.xml",
+    "java/res/layout/page_info_row.xml",
+    "java/res/layout/page_info_v2.xml",
     "java/res/values/colors.xml",
     "java/res/values/dimens.xml",
     "java/res/values/ids.xml",
@@ -71,7 +76,10 @@
     "java/src/org/chromium/components/page_info/PageInfoController.java",
     "java/src/org/chromium/components/page_info/PageInfoControllerDelegate.java",
     "java/src/org/chromium/components/page_info/PageInfoDialog.java",
+    "java/src/org/chromium/components/page_info/PageInfoFeatureList.java",
+    "java/src/org/chromium/components/page_info/PageInfoRowView.java",
     "java/src/org/chromium/components/page_info/PageInfoView.java",
+    "java/src/org/chromium/components/page_info/PageInfoViewV2.java",
     "java/src/org/chromium/components/page_info/SystemSettingsActivityRequiredListener.java",
     "java/src/org/chromium/components/page_info/VrHandler.java",
   ]
@@ -107,6 +115,7 @@
     "java/src/org/chromium/components/page_info/CertificateViewer.java",
     "java/src/org/chromium/components/page_info/ConnectionInfoPopup.java",
     "java/src/org/chromium/components/page_info/PageInfoController.java",
+    "java/src/org/chromium/components/page_info/PageInfoFeatureList.java",
   ]
 }
 
diff --git a/components/page_info/android/features.cc b/components/page_info/android/features.cc
new file mode 100644
index 0000000..e670a67
--- /dev/null
+++ b/components/page_info/android/features.cc
@@ -0,0 +1,15 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/page_info/android/features.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+
+namespace page_info {
+
+const base::Feature kPageInfoV2{"PageInfoV2",
+                                base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace page_info
\ No newline at end of file
diff --git a/components/page_info/android/features.h b/components/page_info/android/features.h
new file mode 100644
index 0000000..5daae79
--- /dev/null
+++ b/components/page_info/android/features.h
@@ -0,0 +1,19 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAGE_INFO_ANDROID_FEATURES_H_
+#define COMPONENTS_PAGE_INFO_ANDROID_FEATURES_H_
+
+namespace base {
+struct Feature;
+}  // namespace base
+
+namespace page_info {
+
+// Enables the second version of the Page Info View.
+extern const base::Feature kPageInfoV2;
+
+}  // namespace page_info
+
+#endif  // COMPONENTS_PAGE_INFO_ANDROID_FEATURES_H_
\ No newline at end of file
diff --git a/components/page_info/android/java/res/layout/page_info_row.xml b/components/page_info/android/java/res/layout/page_info_row.xml
new file mode 100644
index 0000000..0b113bf
--- /dev/null
+++ b/components/page_info/android/java/res/layout/page_info_row.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright 2020 The Chromium Authors. All rights reserved.
+
+   Use of this source code is governed by a BSD-style license that can be
+   found in the LICENSE file.
+-->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingTop="12dp"
+        android:paddingBottom="12dp"
+        android:paddingEnd="@dimen/page_info_popup_padding_sides"
+        android:paddingStart="@dimen/page_info_popup_padding_sides" >
+
+        <ImageView
+            android:id="@+id/page_info_row_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            tools:ignore="ContentDescription"
+            android:layout_marginEnd="10dp"
+            android:layout_centerVertical="true"
+            android:layout_alignParentStart="true"
+            app:tint="@color/default_icon_color" />
+
+        <org.chromium.ui.widget.TextViewWithLeading
+            android:id="@+id/page_info_row_title"
+            android:textAppearance="@style/TextAppearance.TextLarge.Primary"
+            android:layout_toEndOf="@id/page_info_row_icon"
+            android:layout_marginBottom="4dp"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent" />
+
+        <org.chromium.ui.widget.TextViewWithLeading
+            android:id="@+id/page_info_row_subtitle"
+            android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
+            android:layout_toEndOf="@id/page_info_row_icon"
+            android:layout_below="@id/page_info_row_title"
+            app:leading="@dimen/text_size_medium_leading"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent" />
+
+    </RelativeLayout>
+</merge>
\ No newline at end of file
diff --git a/components/page_info/android/java/res/layout/page_info_v2.xml b/components/page_info/android/java/res/layout/page_info_v2.xml
new file mode 100644
index 0000000..cbb4bc2
--- /dev/null
+++ b/components/page_info/android/java/res/layout/page_info_v2.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright 2020 The Chromium Authors. All rights reserved.
+
+   Use of this source code is governed by a BSD-style license that can be
+   found in the LICENSE file.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingBottom="8dp"
+    android:orientation="vertical"
+    android:background="@color/sheet_bg_color">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:paddingBottom="12dp"
+        android:paddingEnd="@dimen/page_info_popup_padding_sides"
+        android:paddingStart="@dimen/page_info_popup_padding_sides" >
+
+        <view class="org.chromium.components.page_info.PageInfoView$ElidedUrlTextView"
+            android:id="@+id/page_info_url"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:lineSpacingExtra="6dp"
+            android:paddingTop="16dp"
+            android:textAlignment="viewStart"
+            android:textAppearance="@style/TextAppearance.TextLarge.Primary" />
+
+        <View
+            android:id="@+id/page_info_preview_separator"
+            android:layout_marginTop="16dp"
+            style="@style/HorizontalDivider"
+            android:visibility="gone" />
+
+        <TextView
+            android:id="@+id/page_info_preview_message"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="14dp"
+            android:textAppearance="@style/TextAppearance.TextLarge.Primary"
+            android:text="@string/page_info_preview_message"
+            android:visibility="gone" />
+
+        <TextView
+            android:id="@+id/page_info_stale_preview_timestamp"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/TextAppearance.TextSmall.Secondary"
+            android:visibility="gone" />
+
+        <TextView
+            android:id="@+id/page_info_preview_load_original"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="12dp"
+            android:textAppearance="@style/TextAppearance.TextLarge.Primary"
+            android:visibility="gone" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/page_info_row_wrapper"
+        android:visibility="gone"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:showDividers="middle"
+        android:divider="?android:listDivider"
+        android:dividerPadding="2dp" >
+        <org.chromium.components.page_info.PageInfoRowView
+            android:id="@+id/page_info_connection_row"
+            android:visibility="gone"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"/>
+        <org.chromium.components.page_info.PageInfoRowView
+            android:id="@+id/page_info_performance_row"
+            android:visibility="gone"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"/>
+        <org.chromium.components.page_info.PageInfoRowView
+            android:id="@+id/page_info_permissions_row"
+            android:visibility="gone"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"/>
+        <org.chromium.components.page_info.PageInfoRowView
+            android:id="@+id/page_info_cookies_row"
+            android:visibility="gone"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"/>
+    </LinearLayout>
+
+    <org.chromium.ui.widget.ButtonCompat
+        android:id="@+id/page_info_instant_app_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:layout_marginEnd="@dimen/page_info_popup_padding_sides"
+        android:layout_marginStart="@dimen/page_info_popup_padding_sides"
+        android:layout_marginTop="12dp"
+        android:layout_marginBottom="4dp"
+        android:paddingEnd="@dimen/page_info_popup_button_padding_sides"
+        android:paddingStart="@dimen/page_info_popup_button_padding_sides"
+        android:text="@string/page_info_instant_app_button"
+        app:buttonColor="@color/app_banner_install_button_bg"
+        style="@style/FilledButton" />
+
+    <org.chromium.ui.widget.ButtonCompat
+        android:id="@+id/page_info_site_settings_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end"
+        android:layout_marginEnd="@dimen/page_info_popup_padding_sides"
+        android:layout_marginStart="@dimen/page_info_popup_padding_sides"
+        android:layout_marginTop="2dp"
+        android:paddingEnd="@dimen/page_info_popup_button_padding_sides"
+        android:paddingStart="@dimen/page_info_popup_button_padding_sides"
+        android:text="@string/page_info_site_settings_button"
+        style="@style/TextButton" />
+
+    <org.chromium.ui.widget.ButtonCompat
+        android:id="@+id/page_info_open_online_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end"
+        android:layout_marginEnd="@dimen/page_info_popup_padding_sides"
+        android:layout_marginStart="@dimen/page_info_popup_padding_sides"
+        android:layout_marginTop="2dp"
+        android:paddingEnd="@dimen/page_info_popup_button_padding_sides"
+        android:paddingStart="@dimen/page_info_popup_button_padding_sides"
+        android:text="@string/page_info_open_online_button"
+        style="@style/TextButton" />
+</LinearLayout>
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/CookieControlsView.java b/components/page_info/android/java/src/org/chromium/components/page_info/CookieControlsView.java
index 0e61a73..d3c25d6 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/CookieControlsView.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/CookieControlsView.java
@@ -29,8 +29,6 @@
 
     /**  Parameters to configure the cookie controls view. */
     public static class CookieControlsParams {
-        // Called when the cookie controls UI is closed.
-        public Runnable onUiClosingCallback;
         // Called when the toggle controlling third-party cookie blocking changes.
         public Callback<Boolean> onCheckedChangedCallback;
     }
@@ -50,13 +48,6 @@
         mParams = params;
     }
 
-    // FrameLayout:
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mParams.onUiClosingCallback.run();
-    }
-
     public void setCookieBlockingStatus(@CookieControlsStatus int status, boolean isEnforced) {
         mLastSwitchState = status == CookieControlsStatus.ENABLED;
         mSwitch.setChecked(mLastSwitchState);
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
index f7c8d14b3..172586fb 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
@@ -112,6 +112,9 @@
     // Reference to last created PageInfoController for testing.
     private static WeakReference<PageInfoController> sLastPageInfoControllerForTesting;
 
+    // Whether Version 2 of the PageInfoView is enabled.
+    private boolean mIsV2Enabled;
+
     /**
      * Creates the PageInfoController, but does not display it. Also initializes the corresponding
      * C++ object and saves a pointer to it.
@@ -123,10 +126,11 @@
      */
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     public PageInfoController(WebContents webContents, int securityLevel, String publisher,
-            PageInfoControllerDelegate delegate) {
+            PageInfoControllerDelegate delegate, boolean isV2Enabled) {
         mWebContents = webContents;
         mSecurityLevel = securityLevel;
         mDelegate = delegate;
+        mIsV2Enabled = isV2Enabled;
         mRunAfterDismissConsumer = new Consumer<Runnable>() {
             @Override
             public void accept(Runnable r) {
@@ -204,6 +208,7 @@
             viewParams.siteSettingsButtonShown = false;
             viewParams.cookieControlsShown = false;
         }
+        viewParams.onUiClosingCallback = mDelegate::onUiClosing;
 
         mDelegate.initPreviewUiParams(viewParams, mRunAfterDismissConsumer);
         mDelegate.initOfflinePageUiParams(viewParams, mRunAfterDismissConsumer);
@@ -224,7 +229,8 @@
             viewParams.instantAppButtonShown = false;
         }
 
-        mView = new PageInfoView(mContext, viewParams);
+        mView = mIsV2Enabled ? new PageInfoViewV2(mContext, viewParams)
+                             : new PageInfoView(mContext, viewParams);
         if (isSheet(mContext)) mView.setBackgroundColor(Color.WHITE);
         // TODO(crbug.com/1040091): Remove when cookie controls are launched.
         boolean showTitle = viewParams.cookieControlsShown;
@@ -232,18 +238,35 @@
                 mWindowAndroid, mFullUrl, showTitle, this, mView::setPermissions);
 
         mNativePageInfoController = PageInfoControllerJni.get().init(this, mWebContents);
+        if (mIsV2Enabled) {
+            PageInfoRowView.ViewParams cookieControlsParams = new PageInfoRowView.ViewParams();
+            cookieControlsParams.visible = viewParams.cookieControlsShown;
+            cookieControlsParams.title =
+                    mView.getContext().getResources().getString(R.string.cookies_title);
+            ((PageInfoViewV2) mView).getCookiesRowView().setParams(cookieControlsParams);
+        } else {
+            CookieControlsView.CookieControlsParams cookieControlsParams =
+                    new CookieControlsView.CookieControlsParams();
+            cookieControlsParams.onCheckedChangedCallback = (Boolean blockCookies) -> {
+                recordAction(blockCookies ? PageInfoAction.PAGE_INFO_COOKIE_BLOCKED_FOR_SITE
+                                          : PageInfoAction.PAGE_INFO_COOKIE_ALLOWED_FOR_SITE);
+                mDelegate.setThirdPartyCookieBlockingEnabledForSite(blockCookies);
+            };
+            mView.getCookieControlsView().setParams(cookieControlsParams);
+        }
         mDelegate.createCookieControlsBridge(this);
-        CookieControlsView.CookieControlsParams cookieControlsParams =
-                new CookieControlsView.CookieControlsParams();
-        cookieControlsParams.onUiClosingCallback = mDelegate::onUiClosing;
-        cookieControlsParams.onCheckedChangedCallback = (Boolean blockCookies) -> {
-            recordAction(blockCookies ? PageInfoAction.PAGE_INFO_COOKIE_BLOCKED_FOR_SITE
-                                      : PageInfoAction.PAGE_INFO_COOKIE_ALLOWED_FOR_SITE);
-            mDelegate.setThirdPartyCookieBlockingEnabledForSite(blockCookies);
-        };
-        mView.getCookieControlsView().setParams(cookieControlsParams);
 
-        mView.showPerformanceInfo(mDelegate.shouldShowPerformanceBadge(mFullUrl));
+        if (mIsV2Enabled) {
+            PageInfoRowView.ViewParams performanceParams = new PageInfoRowView.ViewParams();
+            performanceParams.visible = mDelegate.shouldShowPerformanceBadge(mFullUrl);
+            performanceParams.title = mView.getContext().getResources().getString(
+                    R.string.page_info_fast_site_summary);
+            performanceParams.subtitle = mView.getContext().getResources().getString(
+                    R.string.page_info_fast_site_message);
+            ((PageInfoViewV2) mView).getPerformanceRowView().setParams(performanceParams);
+        } else {
+            mView.showPerformanceInfo(mDelegate.shouldShowPerformanceBadge(mFullUrl));
+        }
 
         mWebContentsObserver = new WebContentsObserver(webContents) {
             @Override
@@ -357,7 +380,21 @@
                 });
             };
         }
-        mView.setConnectionInfo(connectionInfoParams);
+
+        if (mIsV2Enabled) {
+            PageInfoRowView.ViewParams connectionRowParams = new PageInfoRowView.ViewParams();
+            connectionRowParams.title = connectionInfoParams.summary != null
+                    ? connectionInfoParams.summary.toString()
+                    : null;
+            connectionRowParams.subtitle = connectionInfoParams.message != null
+                    ? connectionInfoParams.message.toString()
+                    : null;
+            connectionRowParams.visible =
+                    connectionRowParams.title != null || connectionRowParams.subtitle != null;
+            ((PageInfoViewV2) mView).getConnectionRowView().setParams(connectionRowParams);
+        } else {
+            mView.setConnectionInfo(connectionInfoParams);
+        }
     }
 
     @Override
@@ -448,7 +485,7 @@
 
         sLastPageInfoControllerForTesting = new WeakReference<>(new PageInfoController(webContents,
                 SecurityStateModel.getSecurityLevelForWebContents(webContents), contentPublisher,
-                delegate));
+                delegate, PageInfoFeatureList.isEnabled(PageInfoFeatureList.PAGE_INFO_V2)));
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
@@ -460,13 +497,21 @@
     @Override
     public void onCookieBlockingStatusChanged(
             @CookieControlsStatus int status, @CookieControlsEnforcement int enforcement) {
-        mView.getCookieControlsView().setCookieBlockingStatus(
-                status, enforcement != CookieControlsEnforcement.NO_ENFORCEMENT);
+        if (!mIsV2Enabled) {
+            mView.getCookieControlsView().setCookieBlockingStatus(
+                    status, enforcement != CookieControlsEnforcement.NO_ENFORCEMENT);
+        }
     }
 
     @Override
     public void onBlockedCookiesCountChanged(int blockedCookies) {
-        mView.getCookieControlsView().setBlockedCookiesCount(blockedCookies);
+        if (mIsV2Enabled) {
+            String subtitle = mContext.getResources().getQuantityString(
+                    R.plurals.cookie_controls_blocked_cookies, blockedCookies, blockedCookies);
+            ((PageInfoViewV2) mView).getCookiesRowView().updateSubtitle(subtitle);
+        } else {
+            mView.getCookieControlsView().setBlockedCookiesCount(blockedCookies);
+        }
     }
 
     @NativeMethods
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoFeatureList.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoFeatureList.java
new file mode 100644
index 0000000..6efd7b5
--- /dev/null
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoFeatureList.java
@@ -0,0 +1,44 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.page_info;
+
+import org.chromium.base.FeatureList;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+import org.chromium.base.annotations.NativeMethods;
+
+/**
+ * Provides an API for querying the status of Page Info features.
+ */
+// TODO(crbug.com/1060097): Remove/update this once a generalized FeatureList exists.
+@JNINamespace("page_info")
+@MainDex
+public class PageInfoFeatureList {
+    public static final String PAGE_INFO_V2 = "PageInfoV2";
+
+    private PageInfoFeatureList() {}
+
+    /**
+     * Returns whether the specified feature is enabled or not.
+     *
+     * Note: Features queried through this API must be added to the array
+     * |kFeaturesExposedToJava| in
+     * //components/page_info/android/page_info_feature_list.cc
+     *
+     * @param featureName The name of the feature to query.
+     * @return Whether the feature is enabled or not.
+     */
+    public static boolean isEnabled(String featureName) {
+        Boolean testValue = FeatureList.getTestValueForFeature(featureName);
+        if (testValue != null) return testValue;
+        assert FeatureList.isNativeInitialized();
+        return PageInfoFeatureListJni.get().isEnabled(featureName);
+    }
+
+    @NativeMethods
+    interface Natives {
+        boolean isEnabled(String featureName);
+    }
+}
\ No newline at end of file
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoRowView.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoRowView.java
new file mode 100644
index 0000000..a81ddd5
--- /dev/null
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoRowView.java
@@ -0,0 +1,65 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.page_info;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * View showing an icon, title and subtitle for a page info row.
+ */
+public class PageInfoRowView extends RelativeLayout implements OnClickListener {
+    /**  Parameters to configure the row view. */
+    public static class ViewParams {
+        public boolean visible;
+        public @DrawableRes int iconResId;
+        public String title;
+        public String subtitle;
+        public Runnable clickCallback;
+    }
+
+    private final ImageView mIcon;
+    private final TextView mTitle;
+    private final TextView mSubtitle;
+    private Runnable mClickCallback;
+
+    public PageInfoRowView(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        LayoutInflater.from(context).inflate(R.layout.page_info_row, this, true);
+        mIcon = findViewById(R.id.page_info_row_icon);
+        mTitle = findViewById(R.id.page_info_row_title);
+        mSubtitle = findViewById(R.id.page_info_row_subtitle);
+    }
+
+    public void setParams(ViewParams params) {
+        setVisibility(params.visible ? View.VISIBLE : View.GONE);
+        mIcon.setImageResource(params.iconResId);
+        mTitle.setText(params.title);
+        updateSubtitle(params.subtitle);
+        mClickCallback = params.clickCallback;
+    }
+
+    public void updateSubtitle(String subtitle) {
+        mSubtitle.setText(subtitle);
+        mSubtitle.setVisibility(subtitle != null ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (mClickCallback != null) {
+            mClickCallback.run();
+        }
+    }
+}
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoView.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoView.java
index 81b3a68..6783c5a 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoView.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoView.java
@@ -168,6 +168,7 @@
         public Runnable siteSettingsButtonClickCallback;
         public Runnable openOnlineButtonClickCallback;
         public Runnable previewShowOriginalClickCallback;
+        public Runnable onUiClosingCallback;
 
         public CharSequence url;
         public CharSequence previewLoadOriginalMessage;
@@ -198,77 +199,73 @@
         public Runnable clickCallback;
     }
 
-    private static final int FADE_DURATION_MS = 200;
-    private static final int FADE_IN_BASE_DELAY_MS = 150;
-    private static final int FADE_IN_DELAY_OFFSET_MS = 20;
+    protected static final int FADE_DURATION_MS = 200;
+    protected static final int FADE_IN_BASE_DELAY_MS = 150;
+    protected static final int FADE_IN_DELAY_OFFSET_MS = 20;
 
-    private final ElidedUrlTextView mUrlTitle;
-    private final TextView mConnectionSummary;
-    private final TextView mConnectionMessage;
-    private final TextView mPerformanceSummary;
-    private final TextView mPerformanceMessage;
-    private final TextView mPreviewMessage;
-    private final TextView mPreviewStaleTimestamp;
-    private final TextView mPreviewLoadOriginal;
-    private final TextView mPermissionsTitle;
-    private final View mPermissionsSeparator;
-    private final LinearLayout mPermissionsList;
-    private final View mCookieControlsSeparator;
-    private final CookieControlsView mCookieControlsView;
-    private final View mPreviewSeparator;
-    private final Button mInstantAppButton;
-    private final Button mSiteSettingsButton;
-    private final Button mOpenOnlineButton;
-    private final Runnable mUrlTitleLongClickCallback;
+    // Shared UI components between PageInfoView and PageInfoViewV2. This list should shrink as
+    // these components are replaced with different UI and eventually this class will be replaced
+    // completely.
+    protected ElidedUrlTextView mUrlTitle;
+    protected TextView mPreviewMessage;
+    protected TextView mPreviewStaleTimestamp;
+    protected TextView mPreviewLoadOriginal;
+    protected View mPreviewSeparator;
+    protected Button mInstantAppButton;
+    protected Button mSiteSettingsButton;
+    protected Button mOpenOnlineButton;
+    protected Runnable mUrlTitleLongClickCallback;
+    protected Runnable mOnUiClosingCallback;
+
+    // Components specific to this PageInfoView
+    private TextView mConnectionSummary;
+    private TextView mConnectionMessage;
+    private TextView mPerformanceSummary;
+    private TextView mPerformanceMessage;
+    private TextView mPermissionsTitle;
+    private View mPermissionsSeparator;
+    private LinearLayout mPermissionsList;
+    private View mCookieControlsSeparator;
+    private CookieControlsView mCookieControlsView;
+
+    public PageInfoView(Context context) {
+        super(context);
+    }
 
     public PageInfoView(Context context, PageInfoViewParams params) {
         super(context);
-
-        // Find the container and all it's important subviews.
         LayoutInflater.from(context).inflate(R.layout.page_info, this, true);
-        mUrlTitle = findViewById(R.id.page_info_url);
-        mConnectionSummary = findViewById(R.id.page_info_connection_summary);
-        mConnectionMessage = findViewById(R.id.page_info_connection_message);
-        mPerformanceSummary = findViewById(R.id.page_info_performance_summary);
-        mPerformanceMessage = findViewById(R.id.page_info_performance_message);
-        mPreviewMessage = findViewById(R.id.page_info_preview_message);
-        mPreviewStaleTimestamp = findViewById(R.id.page_info_stale_preview_timestamp);
-        mPreviewLoadOriginal = findViewById(R.id.page_info_preview_load_original);
-        mPermissionsTitle = findViewById(R.id.page_info_permissions_list_title);
-        mPermissionsSeparator = findViewById(R.id.page_info_permissions_separator);
-        mPermissionsList = findViewById(R.id.page_info_permissions_list);
-        mCookieControlsSeparator = findViewById(R.id.page_info_cookie_controls_separator);
-        mCookieControlsView = findViewById(R.id.page_info_cookie_controls_view);
-        mPreviewSeparator = findViewById(R.id.page_info_preview_separator);
-        mInstantAppButton = findViewById(R.id.page_info_instant_app_button);
-        mSiteSettingsButton = findViewById(R.id.page_info_site_settings_button);
-        mOpenOnlineButton = findViewById(R.id.page_info_open_online_button);
+        init(params);
+    }
 
+    protected void init(PageInfoViewParams params) {
+        initUrlTitle(params);
+        initPreview(params);
+        initConnection(params);
+        initPerformance(params);
+        initPermissions(params);
+        initCookies(params);
+        initInstantApp(params);
+        initSiteSettings(params);
+        initOpenOnline(params);
+    }
+
+    protected void initUrlTitle(PageInfoViewParams params) {
+        mUrlTitle = findViewById(R.id.page_info_url);
         mUrlTitle.setUrl(params.url, params.urlOriginLength);
         mUrlTitleLongClickCallback = params.urlTitleLongClickCallback;
         if (params.urlTitleLongClickCallback != null) {
             mUrlTitle.setOnLongClickListener(this);
         }
-
         initializePageInfoViewChild(
                 mUrlTitle, params.urlTitleShown, 0f, params.urlTitleClickCallback);
-        // Hide the connection summary until its text is set.
-        initializePageInfoViewChild(mConnectionSummary, false, 0f, null);
-        initializePageInfoViewChild(mConnectionMessage, params.connectionMessageShown, 0f, null);
-        initializePageInfoViewChild(mPerformanceSummary, false, 0f, null);
-        initializePageInfoViewChild(mPerformanceMessage, false, 0f, null);
-        // Hide the permissions list for sites with no permissions.
-        initializePageInfoViewChild(mPermissionsTitle, false, 0f, null);
-        initializePageInfoViewChild(mPermissionsSeparator, false, 0f, null);
-        initializePageInfoViewChild(mPermissionsList, false, 1f, null);
-        initializePageInfoViewChild(mInstantAppButton, params.instantAppButtonShown, 0f,
-                params.instantAppButtonClickCallback);
-        initializePageInfoViewChild(mSiteSettingsButton, params.siteSettingsButtonShown, 0f,
-                params.siteSettingsButtonClickCallback);
-        // The open online button should not fade in.
-        initializePageInfoViewChild(mOpenOnlineButton, params.openOnlineButtonShown, 1f,
-                params.openOnlineButtonClickCallback);
-        // Previews UI initialization.
+    }
+
+    protected void initPreview(PageInfoViewParams params) {
+        mPreviewMessage = findViewById(R.id.page_info_preview_message);
+        mPreviewStaleTimestamp = findViewById(R.id.page_info_stale_preview_timestamp);
+        mPreviewLoadOriginal = findViewById(R.id.page_info_preview_load_original);
+        mPreviewSeparator = findViewById(R.id.page_info_preview_separator);
         initializePageInfoViewChild(mPreviewMessage, params.previewUIShown, 0f, null);
         initializePageInfoViewChild(mPreviewLoadOriginal, params.previewUIShown, 0f,
                 params.previewShowOriginalClickCallback);
@@ -280,14 +277,71 @@
         if (!TextUtils.isEmpty(params.previewStaleTimestamp)) {
             mPreviewStaleTimestamp.setText(params.previewStaleTimestamp);
         }
+    }
+
+    protected void initConnection(PageInfoViewParams params) {
+        mConnectionSummary = findViewById(R.id.page_info_connection_summary);
+        mConnectionMessage = findViewById(R.id.page_info_connection_message);
+        // Hide the connection summary until its text is set.
+        initializePageInfoViewChild(mConnectionSummary, false, 0f, null);
+        initializePageInfoViewChild(mConnectionMessage, params.connectionMessageShown, 0f, null);
+    }
+
+    protected void initPerformance(PageInfoViewParams params) {
+        mPerformanceSummary = findViewById(R.id.page_info_performance_summary);
+        mPerformanceMessage = findViewById(R.id.page_info_performance_message);
+        initializePageInfoViewChild(mPerformanceSummary, false, 0f, null);
+        initializePageInfoViewChild(mPerformanceMessage, false, 0f, null);
+    }
+
+    protected void initPermissions(PageInfoViewParams params) {
+        mPermissionsTitle = findViewById(R.id.page_info_permissions_list_title);
+        mPermissionsSeparator = findViewById(R.id.page_info_permissions_separator);
+        mPermissionsList = findViewById(R.id.page_info_permissions_list);
+        // Hide the permissions list for sites with no permissions.
+        initializePageInfoViewChild(mPermissionsTitle, false, 0f, null);
+        initializePageInfoViewChild(mPermissionsSeparator, false, 0f, null);
+        initializePageInfoViewChild(mPermissionsList, false, 1f, null);
+    }
+
+    protected void initCookies(PageInfoViewParams params) {
+        mCookieControlsSeparator = findViewById(R.id.page_info_cookie_controls_separator);
+        mCookieControlsView = findViewById(R.id.page_info_cookie_controls_view);
         initializePageInfoViewChild(mCookieControlsSeparator, params.cookieControlsShown, 0f, null);
         initializePageInfoViewChild(mCookieControlsView, params.cookieControlsShown, 0f, null);
+        mOnUiClosingCallback = params.onUiClosingCallback;
+    }
+
+    protected void initInstantApp(PageInfoViewParams params) {
+        mInstantAppButton = findViewById(R.id.page_info_instant_app_button);
+        initializePageInfoViewChild(mInstantAppButton, params.instantAppButtonShown, 0f,
+                params.instantAppButtonClickCallback);
+    }
+
+    protected void initSiteSettings(PageInfoViewParams params) {
+        mSiteSettingsButton = findViewById(R.id.page_info_site_settings_button);
+        initializePageInfoViewChild(mSiteSettingsButton, params.siteSettingsButtonShown, 0f,
+                params.siteSettingsButtonClickCallback);
+    }
+
+    protected void initOpenOnline(PageInfoViewParams params) {
+        mOpenOnlineButton = findViewById(R.id.page_info_open_online_button);
+        // The open online button should not fade in.
+        initializePageInfoViewChild(mOpenOnlineButton, params.openOnlineButtonShown, 1f,
+                params.openOnlineButtonClickCallback);
     }
 
     public CookieControlsView getCookieControlsView() {
         return mCookieControlsView;
     }
 
+    // FrameLayout:
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mOnUiClosingCallback.run();
+    }
+
     public void setPermissions(PermissionParams params) {
         mPermissionsList.removeAllViews();
         // If we have at least one permission show the lower permissions area.
@@ -354,7 +408,7 @@
         return true;
     }
 
-    private void initializePageInfoViewChild(
+    protected void initializePageInfoViewChild(
             View child, boolean shown, float alpha, Runnable clickCallback) {
         // Make all subviews transparent until the page info view is faded in.
         child.setAlpha(alpha);
@@ -401,7 +455,7 @@
     /**
      * Create a list of all the views which we want to individually fade in.
      */
-    private List<View> collectAnimatableViews() {
+    protected List<View> collectAnimatableViews() {
         List<View> animatableViews = new ArrayList<>();
         animatableViews.add(mUrlTitle);
         animatableViews.add(mConnectionSummary);
@@ -428,7 +482,7 @@
     /**
      * Create an animator to fade an individual dialog element.
      */
-    private Animator createInnerFadeAnimation(final View view, int position, boolean isEnter) {
+    protected Animator createInnerFadeAnimation(final View view, int position, boolean isEnter) {
         ObjectAnimator alphaAnim;
 
         if (isEnter) {
@@ -446,7 +500,7 @@
     /**
      * Create animations for fading the view in/out.
      */
-    private Animator createFadeAnimations(boolean isEnter) {
+    protected Animator createFadeAnimations(boolean isEnter) {
         AnimatorSet animation = new AnimatorSet();
         AnimatorSet.Builder builder = animation.play(new AnimatorSet());
 
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoViewV2.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoViewV2.java
new file mode 100644
index 0000000..9b580ff
--- /dev/null
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoViewV2.java
@@ -0,0 +1,92 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.page_info;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents the view inside the second version of the page info popup.
+ */
+public class PageInfoViewV2 extends PageInfoView {
+    // Components specific to this PageInfoView
+    private LinearLayout mRowWrapper;
+    private PageInfoRowView mConnectionRow;
+    private PageInfoRowView mPerformanceRow;
+    private PageInfoRowView mPermissionsRow;
+    private PageInfoRowView mCookiesRow;
+
+    public PageInfoViewV2(Context context, PageInfoView.PageInfoViewParams params) {
+        super(context);
+        LayoutInflater.from(context).inflate(R.layout.page_info_v2, this, true);
+        init(params);
+    }
+
+    @Override
+    protected void init(PageInfoView.PageInfoViewParams params) {
+        super.init(params);
+        mRowWrapper = findViewById(R.id.page_info_row_wrapper);
+        initializePageInfoViewChild(mRowWrapper, true, 0f, null);
+    }
+
+    @Override
+    protected void initUrlTitle(PageInfoView.PageInfoViewParams params) {
+        super.initUrlTitle(params);
+        // Adjust the mUrlTitle
+        mUrlTitle.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
+    }
+
+    @Override
+    protected void initConnection(PageInfoView.PageInfoViewParams params) {
+        mConnectionRow = findViewById(R.id.page_info_connection_row);
+    }
+
+    @Override
+    protected void initPerformance(PageInfoView.PageInfoViewParams params) {
+        mPerformanceRow = findViewById(R.id.page_info_performance_row);
+    }
+
+    @Override
+    protected void initPermissions(PageInfoView.PageInfoViewParams params) {
+        mPermissionsRow = findViewById(R.id.page_info_permissions_row);
+    }
+
+    @Override
+    protected void initCookies(PageInfoView.PageInfoViewParams params) {
+        mCookiesRow = findViewById(R.id.page_info_cookies_row);
+        mOnUiClosingCallback = params.onUiClosingCallback;
+    }
+
+    public PageInfoRowView getConnectionRowView() {
+        return mConnectionRow;
+    }
+
+    public PageInfoRowView getPerformanceRowView() {
+        return mPerformanceRow;
+    }
+
+    public PageInfoRowView getPermissionsRowView() {
+        return mPermissionsRow;
+    }
+
+    public PageInfoRowView getCookiesRowView() {
+        return mCookiesRow;
+    }
+
+    /**
+     * Create a list of all the views which we want to individually fade in.
+     */
+    @Override
+    protected List<View> collectAnimatableViews() {
+        return Arrays.asList(mUrlTitle, mPreviewMessage, mPreviewStaleTimestamp,
+                mPreviewLoadOriginal, mPreviewSeparator, mInstantAppButton, mRowWrapper,
+                mSiteSettingsButton);
+    }
+}
diff --git a/components/page_info/android/page_info_feature_list.cc b/components/page_info/android/page_info_feature_list.cc
new file mode 100644
index 0000000..3e1e0a88
--- /dev/null
+++ b/components/page_info/android/page_info_feature_list.cc
@@ -0,0 +1,46 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_string.h"
+#include "base/feature_list.h"
+#include "base/stl_util.h"
+#include "components/page_info/android/features.h"
+#include "components/page_info/android/jni_headers/PageInfoFeatureList_jni.h"
+
+using base::android::ConvertJavaStringToUTF8;
+using base::android::JavaParamRef;
+
+namespace page_info {
+
+namespace {
+
+// Array of features exposed through the Java ContentFeatureList API. Entries in
+// this array may either refer to features defined in the header of this file or
+// in other locations in the code base (e.g. content_features.h).
+const base::Feature* kFeaturesExposedToJava[] = {
+    &kPageInfoV2,
+};
+
+// TODO(crbug.com/1060097): Remove this once a generalized FeatureList exists.
+const base::Feature* FindFeatureExposedToJava(const std::string& feature_name) {
+  for (size_t i = 0; i < base::size(kFeaturesExposedToJava); ++i) {
+    if (kFeaturesExposedToJava[i]->name == feature_name)
+      return kFeaturesExposedToJava[i];
+  }
+  NOTREACHED() << "Queried feature not found in PageInfoFeatureList: "
+               << feature_name;
+  return nullptr;
+}
+
+}  // namespace
+
+static jboolean JNI_PageInfoFeatureList_IsEnabled(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& jfeature_name) {
+  const base::Feature* feature =
+      FindFeatureExposedToJava(ConvertJavaStringToUTF8(env, jfeature_name));
+  return base::FeatureList::IsEnabled(*feature);
+}
+
+}  // namespace page_info
\ No newline at end of file
diff --git a/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
index c11215f..68ed262 100644
--- a/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
+++ b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
@@ -30,17 +30,6 @@
     if (ukm_features_recorded->find(static_cast<size_t>(feature)) !=
         ukm_features_recorded->end())
       continue;
-    // TODO(kochi): https://crbug.com/806671 https://843080
-    // as ElementCreateShadowRoot is ~8% and
-    // DocumentRegisterElement is ~5% as of May, 2018, to meet UKM's data
-    // volume expectation, reduce the data size by sampling. Revisit and
-    // remove this code once Shadow DOM V0 and Custom Elements V0 are removed.
-    const int kSamplingFactor = 10;
-    if ((feature == WebFeature::kElementCreateShadowRoot ||
-         feature == WebFeature::kDocumentRegisterElement) &&
-        base::RandGenerator(kSamplingFactor) != 0)
-      continue;
-
     ukm::builders::Blink_UseCounter(source_id)
         .SetFeature(static_cast<size_t>(feature))
         .SetIsMainFrameFeature(
diff --git a/components/performance_manager/BUILD.gn b/components/performance_manager/BUILD.gn
index fa84be1..8ab786b 100644
--- a/components/performance_manager/BUILD.gn
+++ b/components/performance_manager/BUILD.gn
@@ -50,6 +50,7 @@
     "graph/page_node_impl_describer.h",
     "graph/policies/process_priority_policy.cc",
     "graph/policies/process_priority_policy.h",
+    "graph/policies/tab_loading_frame_navigation_policy.cc",
     "graph/process_node.cc",
     "graph/process_node_impl.cc",
     "graph/process_node_impl.h",
@@ -96,6 +97,7 @@
     "public/graph/node_data_describer_util.h",
     "public/graph/page_node.h",
     "public/graph/policies/background_tab_loading_policy.h",
+    "public/graph/policies/tab_loading_frame_navigation_policy.h",
     "public/graph/process_node.h",
     "public/graph/system_node.h",
     "public/graph/worker_node.h",
@@ -182,6 +184,7 @@
     "graph/node_base_unittest.cc",
     "graph/page_node_impl_unittest.cc",
     "graph/policies/process_priority_policy_unittest.cc",
+    "graph/policies/tab_loading_frame_navigation_policy_unittest.cc",
     "graph/process_node_impl_unittest.cc",
     "graph/properties_unittest.cc",
     "graph/system_node_impl_unittest.cc",
diff --git a/components/performance_manager/DEPS b/components/performance_manager/DEPS
index 1e6ba322..528b3ad 100644
--- a/components/performance_manager/DEPS
+++ b/components/performance_manager/DEPS
@@ -4,6 +4,7 @@
   "+content/public/test",
   "+content/shell/browser",
   "+mojo/public",
+  "+net/base",
   "+services/metrics/public/cpp",
   "+services/service_manager/public/cpp",
   "+third_party/blink/public/mojom/favicon",
diff --git a/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy.cc b/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy.cc
new file mode 100644
index 0000000..bfd7d3a
--- /dev/null
+++ b/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy.cc
@@ -0,0 +1,298 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/performance_manager/public/graph/policies/tab_loading_frame_navigation_policy.h"
+
+#include "base/bind.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "components/performance_manager/graph/page_node_impl.h"
+#include "components/performance_manager/public/performance_manager.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "url/gurl.h"
+
+namespace performance_manager {
+namespace policies {
+
+namespace {
+
+bool CanThrottleUrlScheme(const GURL& url) {
+  return url.SchemeIs("http") || url.SchemeIs("https");
+}
+
+}  // namespace
+
+TabLoadingFrameNavigationPolicy::TabLoadingFrameNavigationPolicy(
+    StopThrottlingCallback stop_throttling_callback)
+    : stop_throttling_callback_(stop_throttling_callback) {
+  DCHECK(!stop_throttling_callback.is_null());
+}
+
+TabLoadingFrameNavigationPolicy::~TabLoadingFrameNavigationPolicy() {
+  // All timers and timeouts should have been canceled, as no page nodes
+  // should be actively tracked.
+  DCHECK(timeouts_.empty());
+  DCHECK(!timeout_timer_.IsRunning());
+  DCHECK_EQ(base::TimeTicks::Min(), scheduled_timer_);
+}
+
+// static
+bool TabLoadingFrameNavigationPolicy::ShouldThrottleWebContents(
+    content::WebContents* contents) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // Don't throttle unless http or https.
+  bool throttled = true;
+  const GURL& url = contents->GetLastCommittedURL();
+  if (!CanThrottleUrlScheme(url))
+    throttled = false;
+
+  // Post a notification to the graph. Even if we're not throttling the
+  // notification is sent, just in case the WebContents was previously throttled
+  // and is being reused for a new navigation. This is racy, as the policy
+  // object can be destroyed while this message is in flight. This is resolved
+  // on the PM sequence, with the message only being dispatched if the policy
+  // object exists in the graph.
+  PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(&SetPageNodeThrottled,
+                     PerformanceManager::GetPageNodeForWebContents(contents),
+                     throttled));
+
+  return throttled;
+}
+
+// static
+bool TabLoadingFrameNavigationPolicy::ShouldThrottleNavigation(
+    content::NavigationHandle* handle) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // Don't throttle unless http or https.
+  const GURL& url = handle->GetURL();
+  if (!CanThrottleUrlScheme(url))
+    return false;
+
+  // Never throttle the main frame.
+  if (handle->IsInMainFrame())
+    return false;
+
+  // Never throttle frames that are navigating to the same eTLD+1 as the main
+  // frame.
+  auto* contents = handle->GetWebContents();
+  if (net::registry_controlled_domains::SameDomainOrHost(
+          url, contents->GetLastCommittedURL(),
+          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
+    return false;
+  }
+
+  // Throttle any child-frame navigations to a different eTLD+1.
+  return true;
+}
+
+void TabLoadingFrameNavigationPolicy::OnBeforePageNodeRemoved(
+    const PageNode* page_node) {
+  // There's no public graph accessor. We could cache this in OnPassedToGraph,
+  // but it's reachable this way.
+  DCHECK(IsRegistered(page_node->GetGraph()));
+  MaybeErasePageTimeout(page_node);
+}
+
+void TabLoadingFrameNavigationPolicy::OnFirstContentfulPaint(
+    const FrameNode* frame_node,
+    base::TimeDelta time_since_navigation_start) {
+  // There's no public graph accessor. We could cache this in OnPassedToGraph,
+  // but it's reachable this way.
+  DCHECK(IsRegistered(frame_node->GetGraph()));
+
+  // We're only interested in current main-frame FCP notifications.
+  if (!frame_node->IsMainFrame() || !frame_node->IsCurrent())
+    return;
+
+  // Wait for another FCP time period, waiting a minimum of 1 second.
+  double fcp_ms = time_since_navigation_start.InMillisecondsF();
+  double delta_ms = std::max(1000.0, fcp_ms);
+  MaybeUpdatePageTimeout(frame_node->GetPageNode(),
+                         base::TimeDelta::FromMillisecondsD(delta_ms));
+}
+
+void TabLoadingFrameNavigationPolicy::OnPassedToGraph(Graph* graph) {
+  DCHECK(NothingRegistered(graph));
+  graph->AddFrameNodeObserver(this);
+  graph->AddPageNodeObserver(this);
+  graph->RegisterObject(this);
+}
+
+void TabLoadingFrameNavigationPolicy::OnTakenFromGraph(Graph* graph) {
+  DCHECK(IsRegistered(graph));
+  graph->UnregisterObject(this);
+  graph->RemovePageNodeObserver(this);
+  graph->RemoveFrameNodeObserver(this);
+}
+
+// static
+void TabLoadingFrameNavigationPolicy::SetPageNodeThrottled(
+    base::WeakPtr<const PageNode> page_node,
+    bool throttled,
+    Graph* graph) {
+  auto* self = GetFromGraph(graph);
+  if (!self || !page_node)
+    return;
+  self->SetPageNodeThrottledImpl(page_node.get(), throttled);
+}
+
+void TabLoadingFrameNavigationPolicy::SetPageNodeThrottledImpl(
+    const PageNode* page_node,
+    bool throttled) {
+  // There's no public graph accessor. We could cache this in OnPassedToGraph,
+  // but it's reachable this way.
+  DCHECK(IsRegistered(page_node->GetGraph()));
+
+  // It's possible for WebContents to be reused if a main-frame renavigates.
+  // On the UI thread a new scheduler object is created in that case, which will
+  // cause a timeout entry to be (temporarily) orphaned here. So first cleanup
+  // existing timeouts.
+  for (size_t i = 0; i < timeouts_.size(); ++i) {
+    if (timeouts_[i].page_node == page_node) {
+      timeouts_.erase(i);
+      break;
+    }
+  }
+
+  if (!throttled) {
+    MaybeUpdateTimeoutTimer();
+    return;
+  }
+
+  // Create a brand new timeout for the page.
+  // TODO(chrisha): Make this configurable via Finch experiment.
+  CreatePageTimeout(page_node, timeout_);
+}
+
+void TabLoadingFrameNavigationPolicy::CreatePageTimeout(
+    const PageNode* page_node,
+    base::TimeDelta timeout) {
+#if DCHECK_IS_ON()
+  // Sanity check that no timeout entry already exists for this page.
+  for (const auto& timeout : timeouts_) {
+    DCHECK_NE(timeout.page_node, page_node);
+  }
+#endif
+  base::TimeTicks when = base::TimeTicks::Now() + timeout;
+  timeouts_.insert(Timeout{page_node, when});
+  MaybeUpdateTimeoutTimer();
+}
+
+void TabLoadingFrameNavigationPolicy::MaybeUpdatePageTimeout(
+    const PageNode* page_node,
+    base::TimeDelta timeout) {
+  // Find or create an entry for the given |page_node|.
+  size_t i = 0;
+  for (; i < timeouts_.size(); ++i) {
+    if (timeouts_[i].page_node == page_node)
+      break;
+  }
+  if (i == timeouts_.size())
+    return;
+
+  // Update the entry if need be.
+  base::TimeTicks when = base::TimeTicks::Now() + timeout;
+  if (when < timeouts_[i].timeout) {
+    timeouts_.Replace(i, Timeout{page_node, when});
+    MaybeUpdateTimeoutTimer();
+  }
+}
+
+void TabLoadingFrameNavigationPolicy::MaybeErasePageTimeout(
+    const PageNode* page_node) {
+  for (size_t i = 0; i < timeouts_.size(); ++i) {
+    if (timeouts_[i].page_node == page_node) {
+      timeouts_.erase(i);
+      MaybeUpdateTimeoutTimer();
+      return;
+    }
+  }
+}
+
+void TabLoadingFrameNavigationPolicy::MaybeUpdateTimeoutTimer() {
+  if (timeouts_.empty()) {
+    timeout_timer_.Stop();
+    scheduled_timer_ = base::TimeTicks::Min();
+    return;
+  }
+
+  if (timeout_timer_.IsRunning()) {
+    // If the timer is already set to the right time then it doesn't need
+    // updating.
+    if (scheduled_timer_ == timeouts_.top().timeout)
+      return;
+
+    // Otherwise the timer needs rescheduling.
+    scheduled_timer_ = base::TimeTicks::Min();
+    timeout_timer_.Stop();
+  }
+
+  // If the next timeout should already have fired, do so synchronously.
+  base::TimeTicks now = base::TimeTicks::Now();
+  base::TimeTicks when = timeouts_.top().timeout;
+  if (when <= now) {
+    StopThrottlingExpiredPages();
+
+    // Early return if there are no remaining timeouts.
+    if (timeouts_.empty())
+      return;
+  }
+
+  // Restart the timer for the next event.
+  DCHECK(!timeouts_.empty());
+  when = timeouts_.top().timeout;
+  DCHECK_LT(now, when);
+  timeout_timer_.Start(
+      FROM_HERE, when - now,
+      base::BindOnce(&TabLoadingFrameNavigationPolicy::OnTimeout,
+                     base::Unretained(this)));
+  scheduled_timer_ = when;
+}
+
+void TabLoadingFrameNavigationPolicy::OnTimeout() {
+  DCHECK(!timeouts_.empty());
+  timeout_timer_.Stop();
+  scheduled_timer_ = base::TimeTicks::Min();
+  StopThrottlingExpiredPages();
+  MaybeUpdateTimeoutTimer();
+}
+
+void TabLoadingFrameNavigationPolicy::StopThrottlingExpiredPages() {
+  DCHECK(!timeouts_.empty());
+
+  // Send notifications for all expired throttles.
+  auto now = base::TimeTicks::Now();
+  while (timeouts_.size() && now >= timeouts_.top().timeout) {
+    // There's no public graph accessor. We could cache this in OnPassedToGraph,
+    // but it's reachable this way.
+    const PageNode* page_node = timeouts_.top().page_node;
+    DCHECK(IsRegistered(page_node->GetGraph()));
+    timeouts_.pop();
+
+    // Post a task to the UI thread to notify the mechanism to stop throttling
+    // the contents.
+    base::PostTask(
+        FROM_HERE,
+        {content::BrowserThread::UI, base::TaskPriority::USER_VISIBLE},
+        base::BindOnce(
+            [](StopThrottlingCallback stop_throttling_callback,
+               const WebContentsProxy& proxy) {
+              auto* contents = proxy.Get();
+              if (contents)
+                stop_throttling_callback.Run(contents);
+            },
+            stop_throttling_callback_, page_node->GetContentsProxy()));
+  }
+}
+
+}  // namespace policies
+}  // namespace performance_manager
diff --git a/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy_unittest.cc b/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy_unittest.cc
new file mode 100644
index 0000000..ccb6bdc
--- /dev/null
+++ b/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy_unittest.cc
@@ -0,0 +1,317 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/performance_manager/public/graph/policies/tab_loading_frame_navigation_policy.h"
+
+#include "base/bind.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
+#include "components/performance_manager/public/performance_manager.h"
+#include "components/performance_manager/test_support/performance_manager_test_harness.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/mock_navigation_handle.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace performance_manager {
+namespace policies {
+
+namespace {
+
+class TabLoadingFrameNavigationPolicyTest
+    : public PerformanceManagerTestHarness {
+ public:
+  TabLoadingFrameNavigationPolicyTest()
+      : PerformanceManagerTestHarness(
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+  TabLoadingFrameNavigationPolicyTest(
+      const TabLoadingFrameNavigationPolicyTest&) = delete;
+  TabLoadingFrameNavigationPolicyTest& operator=(
+      const TabLoadingFrameNavigationPolicyTest&) = delete;
+  ~TabLoadingFrameNavigationPolicyTest() override = default;
+
+  void SetUp() override {
+    PerformanceManagerTestHarness::SetUp();
+
+    // The unittest fixture outlives the graph under test, so passing an
+    // unretained pointer is safe.
+    auto callback = base::BindRepeating(
+        &TabLoadingFrameNavigationPolicyTest::OnStopThrottlingWrapper,
+        base::Unretained(this));
+
+    // Create the policy object and inject it into the graph.
+    std::unique_ptr<TabLoadingFrameNavigationPolicy> policy =
+        std::make_unique<TabLoadingFrameNavigationPolicy>(callback);
+    policy_ = policy.get();
+    PerformanceManager::PassToGraph(FROM_HERE, std::move(policy));
+
+    // Ensure that the policy default initializes with no throttles in place.
+    ExpectThrottledPageCount(0);
+    start_ = task_environment()->GetMockTickClock()->NowTicks();
+  }
+
+  void RunUntilStopThrottling() {
+    base::RunLoop run_loop;
+    quit_closure_ = run_loop.QuitClosure();
+    run_loop.Run();
+    quit_closure_.Reset();
+  }
+
+  void ExpectThrottledPageCount(size_t expected_throttled_page_count) {
+    base::RunLoop run_loop;
+
+    // This will be called back on the UI thread once the throttled page count
+    // has been retrieved from the PM and validated.
+    auto on_ui_thread = base::BindLambdaForTesting(
+        [expected_throttled_page_count, &run_loop](size_t throttled_page_count,
+                                                   bool timer_running) {
+          EXPECT_EQ(expected_throttled_page_count, throttled_page_count);
+          EXPECT_EQ(expected_throttled_page_count > 0, timer_running);
+          run_loop.Quit();
+        });
+
+    // Call into the graph to get the throttled page count, bounce back to
+    // the UI thread, then continue.
+    auto* policy = policy_;
+    PerformanceManager::CallOnGraph(
+        FROM_HERE,
+        base::BindLambdaForTesting([policy, on_ui_thread](Graph* graph_unused) {
+          size_t throttled_page_count =
+              policy->GetThrottledPageCountForTesting();
+          bool timer_running = policy->IsTimerRunningForTesting();
+
+          // Post back to the UI thread with the answer, where it will be
+          // validated and the run loop exited.
+          base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                         base::BindOnce(on_ui_thread, throttled_page_count,
+                                        timer_running));
+        }));
+
+    // Wait for the response from the graph.
+    run_loop.Run();
+  }
+
+  // Invoked by the policy when throttling should stop for a given contents.
+  MOCK_METHOD1(OnStopThrottling, void(content::WebContents*));
+
+  // Accessors.
+  TabLoadingFrameNavigationPolicy* policy() const { return policy_; }
+  base::TimeTicks start() const { return start_; }
+
+  // The current time, expressed as a multiple of the timeout period.
+  double GetRelativeTime() {
+    base::TimeTicks now = task_environment()->GetMockTickClock()->NowTicks();
+    base::TimeDelta elapsed = now - start_;
+    double relative =
+        elapsed.InSecondsF() / policy_->GetTimeoutForTesting().InSecondsF();
+    return relative;
+  }
+
+ private:
+  void OnStopThrottlingWrapper(content::WebContents* contents) {
+    OnStopThrottling(contents);
+
+    // Time can be manually advanced as well, so we're not always in a RunLoop.
+    // Only try to invoke the quit closure if it has been set.
+    if (!quit_closure_.is_null())
+      quit_closure_.Run();
+  }
+
+  // This is graph owned.
+  TabLoadingFrameNavigationPolicy* policy_ = nullptr;
+  base::RepeatingClosure quit_closure_;
+  base::TimeTicks start_;
+};
+
+}  // namespace
+
+TEST_F(TabLoadingFrameNavigationPolicyTest, OnlyHttpContentsThrottled) {
+  // Only contents with http(s) URLs should be throttled. Anything like
+  // data://, file://, chrome:// or about:// should be ignored.
+
+  // Set the active contents in the RenderViewHostTestHarness.
+  SetContents(CreateTestWebContents());
+
+  {
+    // Expect https:// contents to be throttled.
+    SCOPED_TRACE("https");
+    content::NavigationSimulator::NavigateAndCommitFromBrowser(
+        web_contents(), GURL("https://www.foo.com/"));
+    EXPECT_TRUE(policy()->ShouldThrottleWebContents(web_contents()));
+    ExpectThrottledPageCount(1);
+  }
+
+  {
+    // Expect http:// contents to be throttled.
+    SCOPED_TRACE("http");
+    content::NavigationSimulator::NavigateAndCommitFromBrowser(
+        web_contents(), GURL("http://www.bar.com/"));
+    EXPECT_TRUE(policy()->ShouldThrottleWebContents(web_contents()));
+    ExpectThrottledPageCount(1);
+  }
+
+  {
+    // Expect chrome:// contents not to be throttled.
+    SCOPED_TRACE("chrome");
+    content::NavigationSimulator::NavigateAndCommitFromBrowser(
+        web_contents(), GURL("chrome://settings/"));
+    EXPECT_FALSE(policy()->ShouldThrottleWebContents(web_contents()));
+    ExpectThrottledPageCount(0);
+  }
+
+  {
+    // Expect file:// contents not to be throttled.
+    SCOPED_TRACE("file");
+    content::NavigationSimulator::NavigateAndCommitFromBrowser(
+        web_contents(), GURL("file:///home/chrome/foo.txt"));
+    EXPECT_FALSE(policy()->ShouldThrottleWebContents(web_contents()));
+    ExpectThrottledPageCount(0);
+  }
+
+  {
+    // Expect about:// contents not to be throttled.
+    SCOPED_TRACE("about");
+    content::NavigationSimulator::NavigateAndCommitFromBrowser(
+        web_contents(), GURL("about://blank"));
+    EXPECT_FALSE(policy()->ShouldThrottleWebContents(web_contents()));
+    ExpectThrottledPageCount(0);
+  }
+
+  {
+    // Expect data:// contents not to be throttled.
+    SCOPED_TRACE("data");
+    content::NavigationSimulator::NavigateAndCommitFromBrowser(
+        web_contents(), GURL("data:,Hello%2C%20World!"));
+    EXPECT_FALSE(policy()->ShouldThrottleWebContents(web_contents()));
+    ExpectThrottledPageCount(0);
+  }
+}
+
+TEST_F(TabLoadingFrameNavigationPolicyTest,
+       OnlyAppropriateChildFramesThrottled) {
+  SetContents(CreateTestWebContents());
+  content::NavigationSimulator::NavigateAndCommitFromBrowser(
+      web_contents(), GURL("https://www.foo.com/"));
+
+  auto* main_frame = web_contents()->GetMainFrame();
+
+  // Create a child to the exact same domain. It should not be throttled.
+  auto* child1 =
+      content::RenderFrameHostTester::For(main_frame)->AppendChild("child1");
+  content::MockNavigationHandle handle1(GURL("https://www.foo.com/foo.html"),
+                                        child1);
+  EXPECT_FALSE(policy()->ShouldThrottleNavigation(&handle1));
+
+  // Create a child to the the same eTLD+1.
+  auto* child2 =
+      content::RenderFrameHostTester::For(main_frame)->AppendChild("child2");
+  content::MockNavigationHandle handle2(GURL("https://static.foo.com/foo.html"),
+                                        child2);
+  EXPECT_FALSE(policy()->ShouldThrottleNavigation(&handle2));
+
+  // Create a child to a completely different domain. It should be throttled.
+  auto* child3 =
+      content::RenderFrameHostTester::For(main_frame)->AppendChild("child3");
+  content::MockNavigationHandle handle3(GURL("https://bar.com"), child3);
+  EXPECT_TRUE(policy()->ShouldThrottleNavigation(&handle3));
+
+  // Create a child to a non-HTTP(s) protocol. It should not be throttled.
+  auto* child4 =
+      content::RenderFrameHostTester::For(main_frame)->AppendChild("child4");
+  content::MockNavigationHandle handle4(GURL("data:,Hello%2C%20World!"),
+                                        child4);
+  EXPECT_FALSE(policy()->ShouldThrottleNavigation(&handle4));
+}
+
+TEST_F(TabLoadingFrameNavigationPolicyTest, TimeoutWorks) {
+  // We express times in this test as fractions of a "timeout" period, which
+  // we'll call T. At the beginning of the test, we are at start() == 0.
+  ASSERT_EQ(0.0, GetRelativeTime());
+
+  // Navigate and throttle a first contents. It will expire at time T.
+  std::unique_ptr<content::WebContents> contents1 = CreateTestWebContents();
+  content::NavigationSimulator::NavigateAndCommitFromBrowser(
+      contents1.get(), GURL("http://www.foo1.com/"));
+  EXPECT_TRUE(policy()->ShouldThrottleWebContents(contents1.get()));
+  ExpectThrottledPageCount(1);
+
+  // We expect to still be at time 0.
+  ASSERT_EQ(0.0, GetRelativeTime());
+
+  // Advance time by half of a timeout. No callbacks should fire.
+  task_environment()->FastForwardBy(policy()->GetTimeoutForTesting() * 0.5);
+  testing::Mock::VerifyAndClearExpectations(this);
+
+  // We are now at time T / 2.
+  ASSERT_EQ(0.5, GetRelativeTime());
+
+  // Navigate and throttle a second contents. It will expire at 1.5 T.
+  std::unique_ptr<content::WebContents> contents2 = CreateTestWebContents();
+  content::NavigationSimulator::NavigateAndCommitFromBrowser(
+      contents2.get(), GURL("https://www.foo2.com/"));
+  EXPECT_TRUE(policy()->ShouldThrottleWebContents(contents2.get()));
+  ExpectThrottledPageCount(2);
+
+  // Run until the first contents times out.
+  EXPECT_CALL(*this, OnStopThrottling(contents1.get()));
+  RunUntilStopThrottling();
+  ExpectThrottledPageCount(1);
+  base::TimeTicks stop1 = task_environment()->GetMockTickClock()->NowTicks();
+  EXPECT_EQ(policy()->GetTimeoutForTesting(), stop1 - start());
+
+  // We are now at time T.
+  ASSERT_EQ(1.0, GetRelativeTime());
+
+  // Navigate and throttle a third contents. It will expire at time 2 T.
+  std::unique_ptr<content::WebContents> contents3 = CreateTestWebContents();
+  content::NavigationSimulator::NavigateAndCommitFromBrowser(
+      contents3.get(), GURL("https://www.foo3.com/"));
+  EXPECT_TRUE(policy()->ShouldThrottleWebContents(contents3.get()));
+  ExpectThrottledPageCount(2);
+
+  // Advance time by a quarter of the timeout period, bringing us to time
+  // 1.25 T. This means there will be 1/4 of the timeout left on the second as
+  // it expires at 1.5 T.
+  task_environment()->FastForwardBy(policy()->GetTimeoutForTesting() * 0.25);
+  testing::Mock::VerifyAndClearExpectations(this);
+  ExpectThrottledPageCount(2);
+
+  // We are now at time 1.25 T.
+  ASSERT_EQ(1.25, GetRelativeTime());
+
+  // Close the 2nd contents before the timeout expires, and expect the throttled
+  // count to drop. Now the timer should be running for the 3rd contents.
+  contents2.reset();
+  ExpectThrottledPageCount(1);
+
+  // We are still at time 1.25 T.
+  ASSERT_EQ(1.25, GetRelativeTime());
+
+  // Advance time to a time past when the second notification *would* have
+  // expired, and expect no notifications. We'll go to 1.6 T, so 0.35 T further.
+  task_environment()->FastForwardBy(policy()->GetTimeoutForTesting() * 0.35);
+  testing::Mock::VerifyAndClearExpectations(this);
+
+  // We are now at time 1.6 T.
+  ASSERT_EQ(1.6, GetRelativeTime());
+
+  // Finally, run until the third contents times out.
+  EXPECT_CALL(*this, OnStopThrottling(contents3.get()));
+  RunUntilStopThrottling();
+  ExpectThrottledPageCount(0);
+  base::TimeTicks stop3 = task_environment()->GetMockTickClock()->NowTicks();
+  EXPECT_EQ(policy()->GetTimeoutForTesting(), stop3 - stop1);
+
+  // We are now at time 2 T.
+  ASSERT_EQ(2.0, GetRelativeTime());
+}
+
+}  // namespace policies
+}  // namespace performance_manager
diff --git a/components/performance_manager/public/graph/policies/tab_loading_frame_navigation_policy.h b/components/performance_manager/public/graph/policies/tab_loading_frame_navigation_policy.h
new file mode 100644
index 0000000..ffe9fc9
--- /dev/null
+++ b/components/performance_manager/public/graph/policies/tab_loading_frame_navigation_policy.h
@@ -0,0 +1,170 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_GRAPH_POLICIES_TAB_LOADING_FRAME_NAVIGATION_POLICY_H_
+#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_GRAPH_POLICIES_TAB_LOADING_FRAME_NAVIGATION_POLICY_H_
+
+#include "base/callback.h"
+#include "base/containers/intrusive_heap.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/performance_manager/public/graph/frame_node.h"
+#include "components/performance_manager/public/graph/graph.h"
+#include "components/performance_manager/public/graph/graph_registered.h"
+#include "components/performance_manager/public/graph/page_node.h"
+
+namespace content {
+class WebContents;
+class NavigationHandle;
+}  // namespace content
+
+namespace performance_manager {
+namespace policies {
+
+// The policy half of the TabLoadingFrameNavigation system. The policy engine
+// is responsible for deciding which WebContents will be throttled, and within
+// a WebContents, which frames. Since the decision has to be made synchronously
+// on the UI thread before the PM counterparts exist, this policy exposes
+// helper functions for "pulling" those decisions from the policy engine, rather
+// than having them "pushed" to the mechanism. Once throttling has started the
+// subsequent policy decision of when to stop throttling is pushed to the
+// mechanism as is more typical.
+//
+// At this moment the policy is the following:
+//
+// - Only pages using http or https are throttled.
+// - Main frames are never throttled.
+// - Child frames with the same eTLD+1 as the main frame are not throttled.
+// - All other frames are throttled.
+// - Throttling continues until the main frame hits hits LargestContentfulPaint.
+//   Unfortunately, we can't know LCP in real time, so we use
+//   2 x FirstContentfulPaint as an estimate, with an upper bound of the UMA
+//   measures 95th %ile of LCP.
+//
+// See design document:
+//
+// https://docs.google.com/document/d/141gYr7s2m0rPgzDxzAN0PDMtwT2diHEaa7-sZ0V_Q_M/edit#heading=h.7nki9mck5t64
+class TabLoadingFrameNavigationPolicy
+    : public FrameNode::ObserverDefaultImpl,
+      public GraphOwned,
+      public GraphRegisteredImpl<TabLoadingFrameNavigationPolicy>,
+      public PageNode::ObserverDefaultImpl {
+ public:
+  using StopThrottlingCallback =
+      base::RepeatingCallback<void(content::WebContents*)>;
+
+  // The |stop_throttling_callback| is used to inform the mechanism of when
+  // navigation throttles applied to a WebContents can be released.
+  explicit TabLoadingFrameNavigationPolicy(
+      StopThrottlingCallback stop_throttling_callback);
+  ~TabLoadingFrameNavigationPolicy() override;
+
+  // Exposes policy decisions to the scheduler. This must be called on the UI
+  // thread. If this returns true it is assumed that the |contents| is being
+  // actively scheduled by the corresponding mechanism.
+  static bool ShouldThrottleWebContents(content::WebContents* contents);
+
+  // Exposes policy decisions to the scheduler. This must be called on the UI
+  // thread. If this returns true it is assumed that the |handle| will have a
+  // throttle applies by the corresponding mechanism.
+  static bool ShouldThrottleNavigation(content::NavigationHandle* handle);
+
+  // Exposed for testing. Only safe to call on the PM thread.
+  size_t GetThrottledPageCountForTesting() const { return timeouts_.size(); }
+  bool IsTimerRunningForTesting() const { return timeout_timer_.IsRunning(); }
+
+  // Exposed for testing. Can be called on any sequence, as this is initialized
+  // at construction and stays constant afterwards.
+  base::TimeDelta GetTimeoutForTesting() const { return timeout_; }
+
+ private:
+  // PageNodeObserver:
+  void OnBeforePageNodeRemoved(const PageNode* page_node) override;
+
+  // FrameNodeObserver:
+  void OnFirstContentfulPaint(
+      const FrameNode* frame_node,
+      base::TimeDelta time_since_navigation_start) override;
+
+  // GraphOwned:
+  void OnPassedToGraph(Graph* graph) override;
+  void OnTakenFromGraph(Graph* graph) override;
+
+  // Invoked by ShouldThrottleWebContents logic.
+  static void SetPageNodeThrottled(base::WeakPtr<const PageNode> page_node,
+                                   bool throttled,
+                                   Graph* graph);
+  void SetPageNodeThrottledImpl(const PageNode* page_node, bool throttled);
+
+  // Creates a new page timeout. An entry for |page_node| must not already
+  // exist.
+  void CreatePageTimeout(const PageNode* page_node, base::TimeDelta timeout);
+
+  // Updates the entry for |page_node| if it exists and is for a time later than
+  // |timeout|. Otherwise, does nothing.
+  void MaybeUpdatePageTimeout(const PageNode* page_node,
+                              base::TimeDelta timeout);
+
+  // Erases the entry for |page_node| if it exists.
+  void MaybeErasePageTimeout(const PageNode* page_node);
+
+  // Updates the |timeout_timer_| to reflect the current top of the |timeouts_|
+  // min-heap, or stopping the timer if the heap is empty.
+  void MaybeUpdateTimeoutTimer();
+
+  // Called by the |timeout_timer_| when it fires. This will fire notifications
+  // to the WebContents-affiliated scheduler objects on the UI thread, and then
+  // reschedule a new timer.
+  void OnTimeout();
+
+  // Dispatches StopThrottling notifications for all pages that are currently
+  // expired.
+  void StopThrottlingExpiredPages();
+
+  // A heap of all page nodes that are currently being throttled, sorted
+  // by the time the throttling will timeout. This is intrusive because pages
+  // can be removed before popping out of the heap naturally.
+  struct Timeout {
+    const PageNode* page_node;
+    base::TimeTicks timeout;
+
+    // Reverse the comparison operator so this is a min-heap.
+    bool operator<(const Timeout& rhs) const { return timeout > rhs.timeout; }
+
+    // Dummy implementation of the IntrusiveHeap API. We don't need external
+    // handles into the heap, as we simply look up elements directly and update
+    // them in place.
+    void SetHeapHandle(base::HeapHandle) {}
+    void ClearHeapHandle() {}
+    base::HeapHandle GetHeapHandle() const {
+      return base::HeapHandle::Invalid();
+    }
+  };
+  base::IntrusiveHeap<Timeout> timeouts_;
+
+  // The timeout after which throttling is stopped. This defaults to the 99th
+  // %ile of LargestContentfulPaint (LCP).
+  // TODO(chrisha): Make this Finch configurable.
+  const base::TimeDelta timeout_ = base::TimeDelta::FromSeconds(40);
+
+  // A one shot timer that is used to timeout existing throttles. This will be
+  // running whenever |timeouts_| is not empty.
+  base::OneShotTimer timeout_timer_;
+
+  // The time that has been set using |timeout_timer_|. When |timeouts_| is
+  // empty this is set to "base::TimeTicks::Min()". This is used to know if the
+  // current running timer is okay, or if a new timer is needed (helps to
+  // minimize abandoned timer tasks). We would use
+  // timeout_timer_.desired_run_time(), but this isn't precise.
+  base::TimeTicks scheduled_timer_ = base::TimeTicks::Min();
+
+  // The callback that is invoked to inform the mechanism that throttling should
+  // stop.
+  StopThrottlingCallback stop_throttling_callback_;
+};
+
+}  // namespace policies
+}  // namespace performance_manager
+
+#endif  // COMPONENTS_PERFORMANCE_MANAGER_GRAPH_POLICIES_TAB_LOADING_FRAME_NAVIGATION_POLICY_H_
\ No newline at end of file
diff --git a/components/performance_manager/test_support/performance_manager_test_harness.cc b/components/performance_manager/test_support/performance_manager_test_harness.cc
index 1db3215..36b274c 100644
--- a/components/performance_manager/test_support/performance_manager_test_harness.cc
+++ b/components/performance_manager/test_support/performance_manager_test_harness.cc
@@ -8,14 +8,13 @@
 
 namespace performance_manager {
 
-PerformanceManagerTestHarness::PerformanceManagerTestHarness() {
-  helper_ = std::make_unique<PerformanceManagerTestHarnessHelper>();
-}
+PerformanceManagerTestHarness::PerformanceManagerTestHarness() = default;
 
 PerformanceManagerTestHarness::~PerformanceManagerTestHarness() = default;
 
 void PerformanceManagerTestHarness::SetUp() {
   Super::SetUp();
+  helper_ = std::make_unique<PerformanceManagerTestHarnessHelper>();
   helper_->SetUp();
 }
 
diff --git a/components/performance_manager/test_support/performance_manager_test_harness.h b/components/performance_manager/test_support/performance_manager_test_harness.h
index 3ca097f..d4aa42c 100644
--- a/components/performance_manager/test_support/performance_manager_test_harness.h
+++ b/components/performance_manager/test_support/performance_manager_test_harness.h
@@ -13,14 +13,23 @@
 // A test harness that initializes PerformanceManagerImpl, plus the entire
 // RenderViewHost harness. Allows for creating full WebContents, and their
 // accompanying structures in the graph. The task environment is accessed
-// via content::RenderViewHostTestHarness::test_bundle(). RenderFrameHosts and
-// such are not created, so this is suitable for unittests but not browsertests.
+// via content::RenderViewHostTestHarness::task_environment(). RenderFrameHosts
+// and such are not created, so this is suitable for unittests but not
+// browsertests.
 class PerformanceManagerTestHarness
     : public content::RenderViewHostTestHarness {
  public:
   using Super = content::RenderViewHostTestHarness;
 
   PerformanceManagerTestHarness();
+
+  // Constructs a PerformanceManagerTestHarness which uses |traits| to
+  // initialize its BrowserTaskEnvironment.
+  template <typename... TaskEnvironmentTraits>
+  explicit PerformanceManagerTestHarness(TaskEnvironmentTraits&&... traits)
+      : RenderViewHostTestHarness(
+            std::forward<TaskEnvironmentTraits>(traits)...) {}
+
   PerformanceManagerTestHarness(const PerformanceManagerTestHarness&) = delete;
   PerformanceManagerTestHarness& operator=(
       const PerformanceManagerTestHarness&) = delete;
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index e5b7fe4a..9c0ac07 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -406,6 +406,16 @@
       "cloud/user_cloud_policy_store_unittest.cc",
     ]
   }
+
+  if (is_fuchsia) {
+    sources -= [
+      "cloud/cloud_policy_manager_unittest.cc",
+      "cloud/cloud_policy_validator_unittest.cc",
+      "cloud/machine_level_user_cloud_policy_store_unittest.cc",
+      "cloud/user_cloud_policy_store_unittest.cc",
+    ]
+  }
+
   if (!is_android) {
     sources += [ "async_policy_provider_unittest.cc" ]
   }
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index b4f165b..790ab330 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -14601,7 +14601,7 @@
     {
       'name': 'MinimumChromeVersionEnforced',
       'owners': ['snijhara@google.com'],
-      'supported_on': ['chrome_os:84-'],
+      'supported_on': ['chrome_os:85-'],
       'future': True,
       'device_only': True,
       'type': 'dict',
@@ -18270,24 +18270,17 @@
       'owners': ['services/network/OWNERS'],
       'type': 'main',
       'schema': { 'type': 'boolean' },
-      'supported_on': ['chrome.win:72-'],
+      'supported_on': ['chrome.win:72-83'],
       'features': {
         'dynamic_refresh': False,
         'per_profile': False,
       },
+      'deprecated': True,
       'example_value': False,
       'id': 521,
       'caption': '''Force networking code to run in the browser process''',
       'tags': [],
-      'desc': '''This policy forces networking code to run in the browser process.
-
-      This policy is disabled by default, and if enabled, leaves users open to the security issues once the networking process is sandboxed.
-
-      This policy is intended to give enterprises a chance to migrate to 3rd party software that does not depend on hooking the networking APIs. Proxy servers are recommended over LSPs and Win32 API patching.
-
-      If this policy is not set, networking code will run out of the browser process by default.
-
-      This policy will be removed in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> version 84.''',
+      'desc': '''This policy is deprecated.''',
     },
     {
       'name': 'VoiceInteractionContextEnabled',
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index 912917d..91cad02 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -7,7 +7,7 @@
 Pass at least:
 --chrome-version-file <path to src/chrome/VERSION> or --all-chrome-versions
 --target-platform <which platform the target code will be generated for and can
-  be one of (win, mac, linux, chromeos, fuchsia, ios)>
+  be one of (win, mac, linux, chromeos, ios)>
 --policy_templates <path to the policy_templates.json input file>.'''
 
 
@@ -96,7 +96,6 @@
           'chrome.win',
           'chrome.linux',
           'chrome.mac',
-          'chrome.fuchsia',
           'chrome.*',
           'chrome.win7',
       ]:
@@ -119,7 +118,7 @@
       if platform.startswith('chrome.'):
         platform_sub = platform[7:]
         if platform_sub == '*':
-          self.platforms.extend(['win', 'mac', 'linux', 'fuchsia'])
+          self.platforms.extend(['win', 'mac', 'linux'])
         elif platform_sub == 'win7':
           self.platforms.append('win')
         else:
@@ -1024,24 +1023,25 @@
 
 def _WritePolicyConstantSource(policies, policy_atomic_groups, target_platform,
                                f, risk_tags):
-  f.write('#include "components/policy/policy_constants.h"\n'
-          '\n'
-          '#include <algorithm>\n'
-          '#include <climits>\n'
-          '#include <memory>\n'
-          '\n'
-          '#include "base/logging.h"\n'
-          '#include "base/stl_util.h"  // base::size()\n'
-          '#include "build/branding_buildflags.h"\n'
-          '#include "components/policy/core/common/policy_types.h"\n'
-          '#include "components/policy/core/common/schema_internal.h"\n'
-          '#include "components/policy/proto/cloud_policy.pb.h"\n'
-          '#include "components/policy/risk_tag.h"\n'
-          '\n'
-          'namespace em = enterprise_management;\n\n'
-          '\n'
-          'namespace policy {\n'
-          '\n')
+  f.write('''#include "components/policy/policy_constants.h"
+
+#include <algorithm>
+#include <climits>
+#include <memory>
+
+#include "base/logging.h"
+#include "base/stl_util.h"  // base::size()
+#include "build/branding_buildflags.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/core/common/schema_internal.h"
+#include "components/policy/proto/cloud_policy.pb.h"
+#include "components/policy/risk_tag.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+''')
 
   # Generate the Chrome schema.
   chrome_schema = {
@@ -1065,8 +1065,12 @@
   # Chrome schema, so that binary searching in the PropertyNode array gets the
   # right index on this array as well. See the implementation of
   # GetChromePolicyDetails() below.
-  f.write('const PolicyDetails kChromePolicyDetails[] = {\n'
-          '//  is_deprecated  is_device_policy  id    max_external_data_size\n')
+  # TODO(crbug.com/1074336): kChromePolicyDetails shouldn't be declare if there
+  # is no policy.
+  f.write(
+      '''const __attribute__((unused)) PolicyDetails kChromePolicyDetails[] = {
+//  is_deprecated  is_device_policy  id    max_external_data_size
+''')
   for policy in policies:
     if policy.is_supported:
       assert policy.id >= MIN_POLICY_ID and policy.id <= MAX_POLICY_ID
@@ -1094,14 +1098,17 @@
   schema_generator.FindSensitiveChildren()
   schema_generator.Write(f)
 
-  f.write('\n' 'namespace {\n')
+  f.write('\n')
 
-  f.write('bool CompareKeys(const internal::PropertyNode& node,\n'
-          '                 const std::string& key) {\n'
-          '  return node.key < key;\n'
-          '}\n\n')
+  if schema_generator.property_nodes:
+    f.write('namespace {\n')
 
-  f.write('}  // namespace\n\n')
+    f.write('bool CompareKeys(const internal::PropertyNode& node,\n'
+            '                 const std::string& key) {\n'
+            '  return node.key < key;\n'
+            '}\n\n')
+
+    f.write('}  // namespace\n\n')
 
   if target_platform == 'win':
     f.write('#if BUILDFLAG(GOOGLE_CHROME_BRANDING)\n'
@@ -1148,34 +1155,38 @@
   f.write('}\n' '#endif\n\n')
 
   f.write('const PolicyDetails* GetChromePolicyDetails('
-          'const std::string& policy) {\n'
-          '  // First index in kPropertyNodes of the Chrome policies.\n'
-          '  static const int begin_index = %s;\n'
-          '  // One-past-the-end of the Chrome policies in kPropertyNodes.\n'
-          '  static const int end_index = %s;\n' %
-          (schema_generator.root_properties_begin,
-           schema_generator.root_properties_end))
-  f.write('  const internal::PropertyNode* begin =\n'
-          '      kPropertyNodes + begin_index;\n'
-          '  const internal::PropertyNode* end = kPropertyNodes + end_index;\n'
-          '  const internal::PropertyNode* it =\n'
-          '      std::lower_bound(begin, end, policy, CompareKeys);\n'
-          '  if (it == end || it->key != policy)\n'
-          '    return NULL;\n'
-          '  // This relies on kPropertyNodes from begin_index to end_index\n'
-          '  // having exactly the same policies (and in the same order) as\n'
-          '  // kChromePolicyDetails, so that binary searching on the first\n'
-          '  // gets the same results as a binary search on the second would.\n'
-          '  // However, kPropertyNodes has the policy names and\n'
-          '  // kChromePolicyDetails doesn\'t, so we obtain the index into\n'
-          '  // the second array by searching the first to avoid duplicating\n'
-          '  // the policy name pointers.\n'
-          '  // Offsetting |it| from |begin| here obtains the index we\'re\n'
-          '  // looking for.\n'
-          '  size_t index = it - begin;\n'
-          '  CHECK_LT(index, base::size(kChromePolicyDetails));\n'
-          '  return kChromePolicyDetails + index;\n'
-          '}\n\n')
+          'const std::string& policy) {\n')
+  if schema_generator.property_nodes:
+    f.write('  // First index in kPropertyNodes of the Chrome policies.\n'
+            '  static const int begin_index = %s;\n'
+            '  // One-past-the-end of the Chrome policies in kPropertyNodes.\n'
+            '  static const int end_index = %s;\n' %
+            (schema_generator.root_properties_begin,
+             schema_generator.root_properties_end))
+    f.write('''  const internal::PropertyNode* begin =
+     kPropertyNodes + begin_index;
+  const internal::PropertyNode* end = kPropertyNodes + end_index;
+  const internal::PropertyNode* it =
+      std::lower_bound(begin, end, policy, CompareKeys);
+  if (it == end || it->key != policy)
+    return nullptr;
+  // This relies on kPropertyNodes from begin_index to end_index
+  // having exactly the same policies (and in the same order) as
+  // kChromePolicyDetails, so that binary searching on the first
+  // gets the same results as a binary search on the second would.
+  // However, kPropertyNodes has the policy names and
+  // kChromePolicyDetails doesn't, so we obtain the index into
+  // the second array by searching the first to avoid duplicating
+  // the policy name pointers.
+  // Offsetting |it| from |begin| here obtains the index we're
+  // looking for.
+  size_t index = it - begin;
+  CHECK_LT(index, base::size(kChromePolicyDetails));
+  return kChromePolicyDetails + index;
+''')
+  else:
+    f.write('return nullptr;')
+  f.write('}\n\n')
 
   f.write('namespace key {\n\n')
   for policy in policies:
@@ -1313,27 +1324,27 @@
 
 def _WritePolicyRiskTagHeader(policies, policy_atomic_groups, target_platform,
                               f, risk_tags):
-  f.write('#ifndef CHROME_COMMON_POLICY_RISK_TAG_H_\n'
-          '#define CHROME_COMMON_POLICY_RISK_TAG_H_\n'
-          '\n'
-          '#include <stddef.h>\n'
-          '\n'
-          'namespace policy {\n'
-          '\n' +
-          '// The tag of a policy indicates which impact a policy can have on\n'
-          '// a user\'s privacy and/or security. Ordered descending by \n'
-          '// impact.\n'
-          '// The explanation of the single tags is stated in\n'
-          '// policy_templates.json within the \'risk_tag_definitions\' tag.'
-          '\n' + risk_tags.GenerateEnum() + '\n'
-          '// This constant describes how many risk tags were used by the\n'
-          '// policy which uses the most risk tags. \n'
+  f.write('''#ifndef CHROME_COMMON_POLICY_RISK_TAG_H_
+#define CHROME_COMMON_POLICY_RISK_TAG_H_
+
+#include <stddef.h>
+
+namespace policy {
+
+// The tag of a policy indicates which impact a policy can have on
+// a user's privacy and/or security. Ordered descending by
+// impact.
+// The explanation of the single tags is stated in
+// policy_templates.json within the 'risk_tag_definitions' tag.
+''')
+  f.write(risk_tags.GenerateEnum() + '\n')
+  f.write('// This constant describes how many risk tags were used by the\n'
+          '// policy which uses the most risk tags.\n'
           'const size_t kMaxRiskTagCount = ' + risk_tags.GetMaxTags() + ';\n'
           '\n'
-          '}  // namespace policy\n'
+          '}  // namespace policy\n\n'
           '\n'
-          '#endif  // CHROME_COMMON_POLICY_RISK_TAG_H_'
-          '\n')
+          '#endif  // CHROME_COMMON_POLICY_RISK_TAG_H_')
 
 
 #------------------ policy protobufs -------------------------------#
@@ -1511,7 +1522,7 @@
 
 # Returns the set of all policy.policy_protobuf_type strings from |policies|.
 def _GetProtobufTypes(policies):
-  return set(policy.policy_protobuf_type for policy in policies)
+  return set(['Integer', 'Boolean', 'String', 'StringList'])
 
 
 # Writes the definition of an array that contains the pointers to the mutable
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index d82ca3d..690c362 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -63,6 +63,7 @@
 #include "third_party/blink/public/web/web_plugin.h"
 #include "third_party/blink/public/web/web_plugin_container.h"
 #include "third_party/blink/public/web/web_plugin_document.h"
+#include "third_party/blink/public/web/web_print_page_description.h"
 #include "third_party/blink/public/web/web_print_params.h"
 #include "third_party/blink/public/web/web_print_preset_options.h"
 #include "third_party/blink/public/web/web_script_source.h"
@@ -151,32 +152,31 @@
   PrintMsg_Print_Params page_css_params = page_params;
   int dpi = GetDPI(page_params);
 
-  blink::WebDoubleSize page_size_in_pixels(
+  blink::WebPrintPageDescription description;
+  description.size = blink::WebDoubleSize(
       ConvertUnitDouble(page_params.page_size.width(), dpi, kPixelsPerInch),
       ConvertUnitDouble(page_params.page_size.height(), dpi, kPixelsPerInch));
-  int margin_top_in_pixels =
+  description.margin_top =
       ConvertUnit(page_params.margin_top, dpi, kPixelsPerInch);
-  int margin_right_in_pixels = ConvertUnit(
-      page_params.page_size.width() - page_params.content_size.width() -
-          page_params.margin_left,
-      dpi, kPixelsPerInch);
-  int margin_bottom_in_pixels = ConvertUnit(
+  description.margin_right = ConvertUnit(page_params.page_size.width() -
+                                             page_params.content_size.width() -
+                                             page_params.margin_left,
+                                         dpi, kPixelsPerInch);
+  description.margin_bottom = ConvertUnit(
       page_params.page_size.height() - page_params.content_size.height() -
           page_params.margin_top,
       dpi, kPixelsPerInch);
-  int margin_left_in_pixels =
+  description.margin_left =
       ConvertUnit(page_params.margin_left, dpi, kPixelsPerInch);
 
-  if (frame) {
-    frame->PageSizeAndMarginsInPixels(
-        page_index, page_size_in_pixels, margin_top_in_pixels,
-        margin_right_in_pixels, margin_bottom_in_pixels, margin_left_in_pixels);
-  }
+  if (frame)
+    frame->GetPageDescription(page_index, &description);
 
-  double new_content_width = page_size_in_pixels.Width() -
-                             margin_left_in_pixels - margin_right_in_pixels;
-  double new_content_height = page_size_in_pixels.Height() -
-                              margin_top_in_pixels - margin_bottom_in_pixels;
+  double new_content_width = description.size.Width() -
+                             description.margin_left - description.margin_right;
+  double new_content_height = description.size.Height() -
+                              description.margin_top -
+                              description.margin_bottom;
 
   // Invalid page size and/or margins. We just use the default setting.
   if (new_content_width < 1 || new_content_height < 1) {
@@ -186,16 +186,16 @@
   }
 
   page_css_params.page_size =
-      gfx::Size(ConvertUnit(page_size_in_pixels.Width(), kPixelsPerInch, dpi),
-                ConvertUnit(page_size_in_pixels.Height(), kPixelsPerInch, dpi));
+      gfx::Size(ConvertUnit(description.size.Width(), kPixelsPerInch, dpi),
+                ConvertUnit(description.size.Height(), kPixelsPerInch, dpi));
   page_css_params.content_size =
       gfx::Size(ConvertUnit(new_content_width, kPixelsPerInch, dpi),
                 ConvertUnit(new_content_height, kPixelsPerInch, dpi));
 
   page_css_params.margin_top =
-      ConvertUnit(margin_top_in_pixels, kPixelsPerInch, dpi);
+      ConvertUnit(description.margin_top, kPixelsPerInch, dpi);
   page_css_params.margin_left =
-      ConvertUnit(margin_left_in_pixels, kPixelsPerInch, dpi);
+      ConvertUnit(description.margin_left, kPixelsPerInch, dpi);
   return page_css_params;
 }
 
diff --git a/components/query_tiles/switches.cc b/components/query_tiles/switches.cc
index 78faf7c..0d8e0ec 100644
--- a/components/query_tiles/switches.cc
+++ b/components/query_tiles/switches.cc
@@ -10,6 +10,9 @@
                                 base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kQueryTilesInOmnibox{"QueryTilesInOmnibox",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kQueryTilesEnableQueryEditing{
+    "QueryTilesEnableQueryEditing", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 
 namespace switches {
diff --git a/components/query_tiles/switches.h b/components/query_tiles/switches.h
index 5edb3b7b..7d7098d 100644
--- a/components/query_tiles/switches.h
+++ b/components/query_tiles/switches.h
@@ -16,6 +16,11 @@
 // Feature flag to determine whether query tiles should be shown on omnibox.
 extern const base::Feature kQueryTilesInOmnibox;
 
+// Feature flag to determine whether the user will have a chance to edit the
+// query before in the omnibox sumbitting the search. In this mode only one
+// level of tiles will be displayed.
+extern const base::Feature kQueryTilesEnableQueryEditing;
+
 }  // namespace features
 
 namespace switches {
diff --git a/components/safety_check/BUILD.gn b/components/safety_check/BUILD.gn
index f57e9b7..a52d8552 100644
--- a/components/safety_check/BUILD.gn
+++ b/components/safety_check/BUILD.gn
@@ -33,3 +33,13 @@
     "//testing/gtest",
   ]
 }
+
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "test_update_check_helper.cc",
+    "test_update_check_helper.h",
+  ]
+
+  deps = [ ":safety_check" ]
+}
diff --git a/components/safety_check/test_update_check_helper.cc b/components/safety_check/test_update_check_helper.cc
new file mode 100644
index 0000000..85cf163
--- /dev/null
+++ b/components/safety_check/test_update_check_helper.cc
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safety_check/test_update_check_helper.h"
+
+namespace safety_check {
+
+TestUpdateCheckHelper::TestUpdateCheckHelper() = default;
+
+TestUpdateCheckHelper::~TestUpdateCheckHelper() = default;
+
+void TestUpdateCheckHelper::SetConnectivity(bool online) {
+  online_ = online;
+}
+
+// UpdateCheckHelper implementation.
+void TestUpdateCheckHelper::CheckConnectivity(
+    ConnectivityCheckCallback connection_check_callback) {
+  std::move(connection_check_callback).Run(online_);
+}
+
+}  // namespace safety_check
diff --git a/components/safety_check/test_update_check_helper.h b/components/safety_check/test_update_check_helper.h
new file mode 100644
index 0000000..22ca1f8
--- /dev/null
+++ b/components/safety_check/test_update_check_helper.h
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFETY_CHECK_TEST_UPDATE_CHECK_HELPER_H_
+#define COMPONENTS_SAFETY_CHECK_TEST_UPDATE_CHECK_HELPER_H_
+
+#include "components/safety_check/update_check_helper.h"
+
+namespace safety_check {
+
+// A version of UpdateCheckHelper used for testing.
+class TestUpdateCheckHelper : public UpdateCheckHelper {
+ public:
+  TestUpdateCheckHelper();
+  ~TestUpdateCheckHelper() override;
+
+  void SetConnectivity(bool online);
+
+  // UpdateCheckHelper implementation.
+  void CheckConnectivity(
+      ConnectivityCheckCallback connection_check_callback) override;
+
+ private:
+  bool online_ = true;
+};
+
+}  // namespace safety_check
+
+#endif  // COMPONENTS_SAFETY_CHECK_TEST_UPDATE_CHECK_HELPER_H_
diff --git a/components/safety_check/update_check_helper.cc b/components/safety_check/update_check_helper.cc
index 6834c8b..bb23c72 100644
--- a/components/safety_check/update_check_helper.cc
+++ b/components/safety_check/update_check_helper.cc
@@ -10,6 +10,8 @@
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 #include "url/gurl.h"
 
 namespace safety_check {
@@ -81,6 +83,8 @@
                      base::Unretained(this)));
 }
 
+UpdateCheckHelper::UpdateCheckHelper() = default;
+
 void UpdateCheckHelper::OnURLLoadComplete(
     scoped_refptr<net::HttpResponseHeaders> headers) {
   DCHECK(url_loader_);
diff --git a/components/safety_check/update_check_helper.h b/components/safety_check/update_check_helper.h
index 4aaef19..4f582fd 100644
--- a/components/safety_check/update_check_helper.h
+++ b/components/safety_check/update_check_helper.h
@@ -6,8 +6,12 @@
 #define COMPONENTS_SAFETY_CHECK_UPDATE_CHECK_HELPER_H_
 
 #include "base/callback_forward.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
+#include "net/http/http_response_headers.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+}  // namespace network
 
 namespace safety_check {
 
@@ -17,7 +21,7 @@
  public:
   explicit UpdateCheckHelper(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
-  ~UpdateCheckHelper();
+  virtual ~UpdateCheckHelper();
 
   using ConnectivityCheckCallback = base::OnceCallback<void(bool connected)>;
 
@@ -25,7 +29,12 @@
   // callback with the result. Has a request timeout of 5 seconds. Anything
   // other than the intended HTTP 204 response is considered as no connectivity
   // (user behind proxy, etc).
-  void CheckConnectivity(ConnectivityCheckCallback connection_check_callback);
+  virtual void CheckConnectivity(
+      ConnectivityCheckCallback connection_check_callback);
+
+ protected:
+  // Test-only constructor.
+  UpdateCheckHelper();
 
  private:
   void OnURLLoadComplete(scoped_refptr<net::HttpResponseHeaders> headers);
diff --git a/components/services/language_detection/public/cpp/language_detection_service.cc b/components/services/language_detection/public/cpp/language_detection_service.cc
index 6e242128..00775ee 100644
--- a/components/services/language_detection/public/cpp/language_detection_service.cc
+++ b/components/services/language_detection/public/cpp/language_detection_service.cc
@@ -11,7 +11,6 @@
 mojo::Remote<mojom::LanguageDetectionService> LaunchLanguageDetectionService() {
   return content::ServiceProcessHost::Launch<mojom::LanguageDetectionService>(
       content::ServiceProcessHost::Options()
-          .WithSandboxType(service_manager::SandboxType::kUtility)
           .WithDisplayName("Translate Language Detection")
           .Pass());
 }
diff --git a/components/services/patch/content/patch_service.cc b/components/services/patch/content/patch_service.cc
index b023e6a2d..f88e0a9 100644
--- a/components/services/patch/content/patch_service.cc
+++ b/components/services/patch/content/patch_service.cc
@@ -15,7 +15,6 @@
   content::ServiceProcessHost::Launch<mojom::FilePatcher>(
       remote.InitWithNewPipeAndPassReceiver(),
       content::ServiceProcessHost::Options()
-          .WithSandboxType(service_manager::SandboxType::kUtility)
           .WithDisplayName(IDS_PATCH_SERVICE_DISPLAY_NAME)
           .Pass());
   return remote;
diff --git a/components/services/unzip/content/unzip_service.cc b/components/services/unzip/content/unzip_service.cc
index d664f4fc..8dbfa87 100644
--- a/components/services/unzip/content/unzip_service.cc
+++ b/components/services/unzip/content/unzip_service.cc
@@ -28,7 +28,6 @@
   content::ServiceProcessHost::Launch<mojom::Unzipper>(
       remote.InitWithNewPipeAndPassReceiver(),
       content::ServiceProcessHost::Options()
-          .WithSandboxType(service_manager::SandboxType::kUtility)
           .WithDisplayName(IDS_UNZIP_SERVICE_DISPLAY_NAME)
           .Pass());
   return remote;
diff --git a/components/sync/base/syncer_error.cc b/components/sync/base/syncer_error.cc
index f0af128..15e11bc 100644
--- a/components/sync/base/syncer_error.cc
+++ b/components/sync/base/syncer_error.cc
@@ -39,6 +39,7 @@
     ENUM_CASE(SERVER_RETURN_USER_ROLLBACK);
     ENUM_CASE(SERVER_RETURN_PARTIAL_FAILURE);
     ENUM_CASE(SERVER_RETURN_CLIENT_DATA_OBSOLETE);
+    ENUM_CASE(SERVER_RETURN_ENCRYPTION_OBSOLETE);
     ENUM_CASE(SERVER_MORE_TO_DOWNLOAD);
     ENUM_CASE(DATATYPE_TRIGGERED_RETRY);
     ENUM_CASE(SYNCER_OK);
diff --git a/components/sync/base/syncer_error.h b/components/sync/base/syncer_error.h
index 04edb6ed..a469d855 100644
--- a/components/sync/base/syncer_error.h
+++ b/components/sync/base/syncer_error.h
@@ -40,6 +40,7 @@
     SERVER_RETURN_USER_ROLLBACK,
     SERVER_RETURN_PARTIAL_FAILURE,
     SERVER_RETURN_CLIENT_DATA_OBSOLETE,
+    SERVER_RETURN_ENCRYPTION_OBSOLETE,
 
     // A datatype decided the sync cycle needed to be performed again.
     DATATYPE_TRIGGERED_RETRY,
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index cd4d4bb8..cea197a 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -67,15 +67,15 @@
 // decryption, or the version of Chrome being too old. This enum is used to
 // back a UMA histogram, and should therefore be treated as append-only.
 enum SyncInitialState {
-  CAN_START,                // Sync can attempt to start up.
-  NOT_SIGNED_IN,            // There is no signed in user.
-  NOT_REQUESTED,            // The user turned off sync.
-  NOT_REQUESTED_NOT_SETUP,  // The user turned off sync and setup completed
-                            // is false. Might indicate a stop-and-clear.
-  NEEDS_CONFIRMATION,       // The user must confirm sync settings.
-  NOT_ALLOWED_BY_POLICY,    // Sync is disallowed by enterprise policy.
-  NOT_ALLOWED_BY_PLATFORM,  // Sync is disallowed by the platform.
-  SYNC_INITIAL_STATE_LIMIT
+  CAN_START = 0,                // Sync can attempt to start up.
+  NOT_SIGNED_IN = 1,            // There is no signed in user.
+  NOT_REQUESTED = 2,            // The user turned off sync.
+  NOT_REQUESTED_NOT_SETUP = 3,  // The user turned off sync and setup completed
+                                // is false. Might indicate a stop-and-clear.
+  NEEDS_CONFIRMATION = 4,       // The user must confirm sync settings.
+  NOT_ALLOWED_BY_POLICY = 5,    // Sync is disallowed by enterprise policy.
+  OBSOLETE_NOT_ALLOWED_BY_PLATFORM = 6,
+  kMaxValue = OBSOLETE_NOT_ALLOWED_BY_PLATFORM
 };
 
 // These values are persisted to logs. Entries should not be renumbered and
@@ -102,12 +102,6 @@
                  ProfileSyncService::DISABLE_REASON_ENTERPRISE_POLICY)) {
     sync_state = NOT_ALLOWED_BY_POLICY;
   } else if (disable_reasons.Has(
-                 ProfileSyncService::DISABLE_REASON_PLATFORM_OVERRIDE)) {
-    // This case means Android's "MasterSync" toggle. However, that is not
-    // plumbed into ProfileSyncService until after this method, so we never get
-    // here. See http://crbug.com/568771.
-    sync_state = NOT_ALLOWED_BY_PLATFORM;
-  } else if (disable_reasons.Has(
                  ProfileSyncService::DISABLE_REASON_USER_CHOICE)) {
     if (first_setup_complete) {
       sync_state = NOT_REQUESTED;
@@ -117,8 +111,7 @@
   } else if (!first_setup_complete) {
     sync_state = NEEDS_CONFIRMATION;
   }
-  UMA_HISTOGRAM_ENUMERATION("Sync.InitialState", sync_state,
-                            SYNC_INITIAL_STATE_LIMIT);
+  base::UmaHistogramEnumeration("Sync.InitialState", sync_state);
 }
 
 constexpr char kSyncUnrecoverableErrorHistogram[] = "Sync.UnrecoverableErrors";
diff --git a/components/sync/engine_impl/sync_scheduler_impl.cc b/components/sync/engine_impl/sync_scheduler_impl.cc
index 3e6f993..1f0377d8 100644
--- a/components/sync/engine_impl/sync_scheduler_impl.cc
+++ b/components/sync/engine_impl/sync_scheduler_impl.cc
@@ -59,6 +59,7 @@
     case CLIENT_DATA_OBSOLETE:
     case CLEAR_PENDING:
     case DISABLED_BY_ADMIN:
+    case ENCRYPTION_OBSOLETE:
       // If we send terminate sync early then |sync_cycle_ended| notification
       // would not be sent. If there were no actions then |ACTIONABLE_ERROR|
       // notification wouldnt be sent either. Then the UI layer would be left
@@ -68,6 +69,8 @@
     // Make UNKNOWN_ERROR a NOTREACHED. All the other error should be explicitly
     // handled.
     case UNKNOWN_ERROR:
+      // TODO(crbug.com/1081266): This NOTREACHED is questionable because the
+      // sync server can cause it.
       NOTREACHED();
       return false;
   }
diff --git a/components/sync/engine_impl/syncer_proto_util.cc b/components/sync/engine_impl/syncer_proto_util.cc
index 91194ec..3173ed12 100644
--- a/components/sync/engine_impl/syncer_proto_util.cc
+++ b/components/sync/engine_impl/syncer_proto_util.cc
@@ -130,10 +130,18 @@
       return CLIENT_DATA_OBSOLETE;
     case sync_pb::SyncEnums::UNKNOWN:
       return UNKNOWN_ERROR;
-    default:
-      NOTREACHED();
+    case sync_pb::SyncEnums::ENCRYPTION_OBSOLETE:
+      return ENCRYPTION_OBSOLETE;
+    case sync_pb::SyncEnums::DEPRECATED_ACCESS_DENIED:
+    case sync_pb::SyncEnums::DEPRECATED_AUTH_EXPIRED:
+    case sync_pb::SyncEnums::DEPRECATED_AUTH_INVALID:
+    case sync_pb::SyncEnums::DEPRECATED_USER_NOT_ACTIVATED:
+    case sync_pb::SyncEnums::DEPRECATED_USER_ROLLBACK:
       return UNKNOWN_ERROR;
   }
+
+  NOTREACHED();
+  return UNKNOWN_ERROR;
 }
 
 ClientAction PBActionToClientAction(const sync_pb::SyncEnums::Action& action) {
@@ -146,10 +154,10 @@
     case sync_pb::SyncEnums::DEPRECATED_DISABLE_SYNC_ON_CLIENT:
     case sync_pb::SyncEnums::UNKNOWN_ACTION:
       return UNKNOWN_ACTION;
-    default:
-      NOTREACHED();
-      return UNKNOWN_ACTION;
   }
+
+  NOTREACHED();
+  return UNKNOWN_ACTION;
 }
 
 // Returns true iff |message| is an initial GetUpdates request.
@@ -181,7 +189,8 @@
   SyncProtocolError error;
   error.error_type = PBErrorTypeToSyncProtocolErrorType(error_type);
   if (error_type == sync_pb::SyncEnums::CLEAR_PENDING ||
-      error_type == sync_pb::SyncEnums::NOT_MY_BIRTHDAY) {
+      error_type == sync_pb::SyncEnums::NOT_MY_BIRTHDAY ||
+      error_type == sync_pb::SyncEnums::ENCRYPTION_OBSOLETE) {
     error.action = DISABLE_SYNC_ON_CLIENT;
   } else if (error_type == sync_pb::SyncEnums::CLIENT_DATA_OBSOLETE) {
     error.action = RESET_LOCAL_SYNC_DATA;
@@ -308,6 +317,13 @@
     // Legacy server implementation. Compute the error based on |error_code|.
     sync_protocol_error = ErrorCodeToSyncProtocolError(response.error_code());
   }
+
+  // Trivially inferred actions.
+  if (sync_protocol_error.action == UNKNOWN_ACTION &&
+      sync_protocol_error.error_type == ENCRYPTION_OBSOLETE) {
+    sync_protocol_error.action = DISABLE_SYNC_ON_CLIENT;
+  }
+
   return sync_protocol_error;
 }
 
@@ -556,10 +572,12 @@
       return SyncerError(SyncerError::SYNCER_OK);
     case CLIENT_DATA_OBSOLETE:
       return SyncerError(SyncerError::SERVER_RETURN_CLIENT_DATA_OBSOLETE);
-    default:
-      NOTREACHED();
-      return SyncerError();
+    case ENCRYPTION_OBSOLETE:
+      return SyncerError(SyncerError::SERVER_RETURN_ENCRYPTION_OBSOLETE);
   }
+
+  NOTREACHED();
+  return SyncerError();
 }
 
 // static
diff --git a/components/sync/engine_impl/syncer_proto_util_unittest.cc b/components/sync/engine_impl/syncer_proto_util_unittest.cc
index 55aaebb..8c33d64b 100644
--- a/components/sync/engine_impl/syncer_proto_util_unittest.cc
+++ b/components/sync/engine_impl/syncer_proto_util_unittest.cc
@@ -206,6 +206,17 @@
   EXPECT_EQ(STOP_SYNC_FOR_DISABLED_ACCOUNT, sync_protocol_error.action);
 }
 
+TEST_F(SyncerProtoUtilTest, VerifyEncryptionObsolete) {
+  sync_pb::ClientToServerResponse response;
+  response.set_error_code(sync_pb::SyncEnums::ENCRYPTION_OBSOLETE);
+  response.set_store_birthday("flan");
+
+  SyncProtocolError sync_protocol_error =
+      CallGetProtocolErrorFromResponse(response, context());
+  EXPECT_EQ(ENCRYPTION_OBSOLETE, sync_protocol_error.error_type);
+  EXPECT_EQ(DISABLE_SYNC_ON_CLIENT, sync_protocol_error.action);
+}
+
 class DummyConnectionManager : public ServerConnectionManager {
  public:
   DummyConnectionManager() : send_error_(false) {}
diff --git a/components/sync/protocol/proto_enum_conversions.cc b/components/sync/protocol/proto_enum_conversions.cc
index 0e4dd00..9a92ef38 100644
--- a/components/sync/protocol/proto_enum_conversions.cc
+++ b/components/sync/protocol/proto_enum_conversions.cc
@@ -196,6 +196,7 @@
     ENUM_CASE(sync_pb::SyncEnums, DEPRECATED_USER_ROLLBACK);
     ENUM_CASE(sync_pb::SyncEnums, PARTIAL_FAILURE);
     ENUM_CASE(sync_pb::SyncEnums, CLIENT_DATA_OBSOLETE);
+    ENUM_CASE(sync_pb::SyncEnums, ENCRYPTION_OBSOLETE);
     ENUM_CASE(sync_pb::SyncEnums, UNKNOWN);
   }
   NOTREACHED();
diff --git a/components/sync/protocol/sync_enums.proto b/components/sync/protocol/sync_enums.proto
index d13cfba..9cea98e 100644
--- a/components/sync/protocol/sync_enums.proto
+++ b/components/sync/protocol/sync_enums.proto
@@ -87,35 +87,42 @@
   enum ErrorType {
     SUCCESS = 0;
     DEPRECATED_ACCESS_DENIED = 1;
-    NOT_MY_BIRTHDAY = 2;  // Returned when the server and client disagree
-                          // on the store birthday.
-    THROTTLED = 3;        // Returned when the store has exceeded the
-                          // allowed bandwidth utilization.
+    // Returned when the server and client disagree on the store birthday.
+    NOT_MY_BIRTHDAY = 2;
+    // Returned when the store has exceeded the allowed bandwidth utilization.
+    THROTTLED = 3;
     DEPRECATED_AUTH_EXPIRED = 4;
     DEPRECATED_USER_NOT_ACTIVATED = 5;
     DEPRECATED_AUTH_INVALID = 6;
-    CLEAR_PENDING = 7;       // A clear of the user data is pending (e.g.
-                             // initiated by privacy request).  Client should
-                             // come back later.
-    TRANSIENT_ERROR = 8;     // A transient error occured (eg. backend
-                             // timeout). Client should try again later.
-    MIGRATION_DONE = 9;      // Migration has finished for one or more data
-                             // types.  Client should clear the cache for
-                             // these data types only and then re-sync with
-                             // a server.
-    DISABLED_BY_ADMIN = 10;  // An administrator disabled sync for this
-                             // domain.
-    DEPRECATED_USER_ROLLBACK = 11;  // Deprecated in M50.
-    PARTIAL_FAILURE = 12;           // Return when client want to update several
-                                    // data types, but some of them failed(e.g.
-                                    // throttled).
-    CLIENT_DATA_OBSOLETE = 13;      // Returned when server detects that this
-                                    // client's data is obsolete. Client should
-                                    // reset local data and restart syncing.
-    UNKNOWN = 100;                  // Unknown value. This should never be
-    // explicitly used; it is the default value when
-    // an out-of-date client parses a value it
-    // doesn't recognize.
+    // A clear of the user data is pending (e.g. initiated by privacy request).
+    // The client should come back later.
+    CLEAR_PENDING = 7;
+    // A transient error occurred (e.g. a backend timeout). The client should
+    // try again later.
+    TRANSIENT_ERROR = 8;
+    // A server-side migration has taken place for one or more data types. The
+    // client should clear the cache for these data types only and then re-sync
+    // with a server.
+    MIGRATION_DONE = 9;
+    // An administrator disabled sync for this domain.
+    DISABLED_BY_ADMIN = 10;
+    // Deprecated in M50.
+    DEPRECATED_USER_ROLLBACK = 11;
+    // Returned when the client wants to update several data types, but some of
+    // them failed (e.g. throttled).
+    PARTIAL_FAILURE = 12;
+    // Returned when the server detects that this client's sync metadata is
+    // obsolete. The client should reset local sync metadata and restart
+    // syncing.
+    CLIENT_DATA_OBSOLETE = 13;
+    // Returned when the server detects that the encryption state (Nigori,
+    // keystore keys) has been reset/overridden, which means the local
+    // Nigori-related state is obsolete and should be cleared.
+    // Introduced in M84.
+    ENCRYPTION_OBSOLETE = 14;
+    // Unknown value. This should never be explicitly used; it is the default
+    // value when an out-of-date client parses a value it doesn't recognize.
+    UNKNOWN = 100;
   }
 
   enum Action {
diff --git a/components/sync/protocol/sync_protocol_error.cc b/components/sync/protocol/sync_protocol_error.cc
index 9739564..0ba670c 100644
--- a/components/sync/protocol/sync_protocol_error.cc
+++ b/components/sync/protocol/sync_protocol_error.cc
@@ -25,6 +25,7 @@
     ENUM_CASE(DISABLED_BY_ADMIN);
     ENUM_CASE(PARTIAL_FAILURE);
     ENUM_CASE(CLIENT_DATA_OBSOLETE);
+    ENUM_CASE(ENCRYPTION_OBSOLETE);
     ENUM_CASE(UNKNOWN_ERROR);
   }
   NOTREACHED();
diff --git a/components/sync/protocol/sync_protocol_error.h b/components/sync/protocol/sync_protocol_error.h
index 08019aa1..5101433 100644
--- a/components/sync/protocol/sync_protocol_error.h
+++ b/components/sync/protocol/sync_protocol_error.h
@@ -42,6 +42,11 @@
   // should reset local data and restart syncing.
   CLIENT_DATA_OBSOLETE,
 
+  // Returned when the server detects that the encryption state (Nigori,
+  // keystore keys) has been reset/overridden, which means the local
+  // Nigori-related state is obsolete and should be cleared.
+  ENCRYPTION_OBSOLETE,
+
   // The default value.
   UNKNOWN_ERROR
 };
diff --git a/components/test/data/autofill_assistant/html/autofill_assistant_target_website_iframe_one.html b/components/test/data/autofill_assistant/html/autofill_assistant_target_website_iframe_one.html
index 050ee52..332702a 100644
--- a/components/test/data/autofill_assistant/html/autofill_assistant_target_website_iframe_one.html
+++ b/components/test/data/autofill_assistant/html/autofill_assistant_target_website_iframe_one.html
@@ -62,7 +62,7 @@
       <form name="address" id="address_section">
         <div id="billing">
           <h2>Billing Address</h2>
-          Name: <input type="text" name="name"><br>
+          Name: <input type="text" name="name" id="name"><br>
           Address: <input type="text" name="address"><br>
           City: <input type="text" name="city"><br>
           State: <select name="state">
diff --git a/components/test/data/autofill_assistant/html/form_target_website.html b/components/test/data/autofill_assistant/html/form_target_website.html
index c159db2..2221b6d 100644
--- a/components/test/data/autofill_assistant/html/form_target_website.html
+++ b/components/test/data/autofill_assistant/html/form_target_website.html
@@ -28,7 +28,7 @@
     <div id="terms" class="terms">
       <button id="button" onclick="onClick()"> Button </button>
       <div id="toggle_on" style="display: none"> Toggle on </div>
-  </div>
+    </div>
 
     <h4>Credit card</h4>
     <div>
@@ -80,5 +80,7 @@
       </form>
     </div>
 
+    <iframe id="iframe" name="test_iframe" width="100%" height="500" src=
+        "autofill_assistant_target_website_iframe_one.html"></iframe>
   </body>
 </html>
diff --git a/components/url_formatter/url_fixer_unittest.cc b/components/url_formatter/url_fixer_unittest.cc
index 9c2acd7..607cee3 100644
--- a/components/url_formatter/url_fixer_unittest.cc
+++ b/components/url_formatter/url_fixer_unittest.cc
@@ -515,7 +515,7 @@
 
     // Much of the work here comes from GURL's canonicalization stage.
     {"file://C:/foo/bar", "file:///C:/foo/bar"},
-    {"file:c:", "file:///C:/"},
+    {"file:c:", "file:///C:"},
     {"file:c:WINDOWS", "file:///C:/WINDOWS"},
     {"file:c|Program Files", "file:///C:/Program%20Files"},
     {"file:/file", "file://file/"},
diff --git a/components/webrtc/android/BUILD.gn b/components/webrtc/android/BUILD.gn
index 1c74c40..a335ac2 100644
--- a/components/webrtc/android/BUILD.gn
+++ b/components/webrtc/android/BUILD.gn
@@ -14,10 +14,6 @@
     "//base:base_java",
     "//components/browser_ui/notifications/android:java",
     "//third_party/android_deps:androidx_core_core_java",
-
-    # TODO(crbug.com/1017190): Remove the following deps once we stop linting individual targets.
-    "//components/browser_ui/styles/android:java_resources",
-    "//ui/android:ui_java",
   ]
 }
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 207b641..1732842 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -276,6 +276,8 @@
     "$target_gen_dir/devtools/protocol/inspector.h",
     "$target_gen_dir/devtools/protocol/io.cc",
     "$target_gen_dir/devtools/protocol/io.h",
+    "$target_gen_dir/devtools/protocol/log.cc",
+    "$target_gen_dir/devtools/protocol/log.h",
     "$target_gen_dir/devtools/protocol/memory.cc",
     "$target_gen_dir/devtools/protocol/memory.h",
     "$target_gen_dir/devtools/protocol/network.cc",
@@ -775,6 +777,8 @@
     "devtools/protocol/inspector_handler.h",
     "devtools/protocol/io_handler.cc",
     "devtools/protocol/io_handler.h",
+    "devtools/protocol/log_handler.cc",
+    "devtools/protocol/log_handler.h",
     "devtools/protocol/memory_handler.cc",
     "devtools/protocol/memory_handler.h",
     "devtools/protocol/native_input_event_builder.h",
diff --git a/content/browser/appcache/appcache_host.cc b/content/browser/appcache/appcache_host.cc
index eef8c30..4268404 100644
--- a/content/browser/appcache/appcache_host.cc
+++ b/content/browser/appcache/appcache_host.cc
@@ -217,7 +217,8 @@
 
     AppCachePolicy* policy = service()->appcache_policy();
     if (policy && !policy->CanCreateAppCache(
-                      manifest_url, site_for_cookies_.RepresentativeUrl())) {
+                      manifest_url, site_for_cookies_.RepresentativeUrl(),
+                      top_frame_origin_)) {
       FinishCacheSelection(nullptr, nullptr, mojo::ReportBadMessageCallback());
       frontend()->EventRaised(
           blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
@@ -406,6 +407,7 @@
     // for checking whether the creation of the appcache is allowed.
     site_for_cookies_ = request->GetSiteForCookies();
     site_for_cookies_initialized_ = true;
+    top_frame_origin_ = request->GetTopFrameOrigin();
     return base::WrapUnique(new AppCacheRequestHandler(
         this, resource_type, should_reset_appcache, std::move(request)));
   }
diff --git a/content/browser/appcache/appcache_host.h b/content/browser/appcache/appcache_host.h
index a3743b4..111775d 100644
--- a/content/browser/appcache/appcache_host.h
+++ b/content/browser/appcache/appcache_host.h
@@ -17,6 +17,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/optional.h"
 #include "content/browser/appcache/appcache_group.h"
 #include "content/browser/appcache/appcache_service_impl.h"
 #include "content/browser/appcache/appcache_storage.h"
@@ -229,6 +230,10 @@
     site_for_cookies_initialized_ = true;
   }
 
+  const base::Optional<url::Origin>& top_frame_origin() const {
+    return top_frame_origin_;
+  }
+
   void set_origin_for_url_loader_factory(const url::Origin& origin) {
     origin_for_url_loader_factory_ = origin;
   }
@@ -413,6 +418,7 @@
   // To be used in policy checks.
   net::SiteForCookies site_for_cookies_;
   bool site_for_cookies_initialized_ = false;
+  base::Optional<url::Origin> top_frame_origin_;
 
   bool is_origin_trial_required_ = false;
 
diff --git a/content/browser/appcache/appcache_policy.h b/content/browser/appcache/appcache_policy.h
index f864417..93ce034 100644
--- a/content/browser/appcache/appcache_policy.h
+++ b/content/browser/appcache/appcache_policy.h
@@ -5,8 +5,14 @@
 #ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_POLICY_H_
 #define CONTENT_BROWSER_APPCACHE_APPCACHE_POLICY_H_
 
+#include "base/optional.h"
+
 class GURL;
 
+namespace url {
+class Origin;
+}
+
 namespace content {
 
 class AppCachePolicy {
@@ -16,12 +22,18 @@
   // Called prior to loading a main resource from the appache.
   // Returns true if allowed. This is expected to return immediately
   // without any user prompt.
-  virtual bool CanLoadAppCache(const GURL& manifest_url,
-                               const GURL& first_party) = 0;
+  virtual bool CanLoadAppCache(
+      const GURL& manifest_url,
+
+      const GURL& site_for_cookies,
+      const base::Optional<url::Origin>& top_frame_origin) = 0;
 
   // Called prior to creating a new appcache. Returns true if allowed.
-  virtual bool CanCreateAppCache(const GURL& manifest_url,
-                                 const GURL& first_party) = 0;
+  virtual bool CanCreateAppCache(
+      const GURL& manifest_url,
+
+      const GURL& site_for_cookies,
+      const base::Optional<url::Origin>& top_frame_origin) = 0;
 
   // Returns true if origin trial tokens are required in order to fetch or
   // update manifests, as well as load any resources from such a manifest.
diff --git a/content/browser/appcache/appcache_request.cc b/content/browser/appcache/appcache_request.cc
index 598c6e78..a19a46c 100644
--- a/content/browser/appcache/appcache_request.cc
+++ b/content/browser/appcache/appcache_request.cc
@@ -5,9 +5,11 @@
 #include "content/browser/appcache/appcache_request.h"
 
 #include "content/common/appcache_interfaces.h"
+#include "net/base/isolation_info.h"
 #include "net/url_request/redirect_info.h"
 #include "net/url_request/redirect_util.h"
 #include "net/url_request/url_request.h"
+#include "url/origin.h"
 
 namespace content {
 
@@ -30,6 +32,12 @@
   return 0;
 }
 
+base::Optional<url::Origin> AppCacheRequest::GetTopFrameOrigin() const {
+  return request_.trusted_params
+             ? request_.trusted_params->isolation_info.top_frame_origin()
+             : base::nullopt;
+}
+
 std::string AppCacheRequest::GetResponseHeaderByName(
     const std::string& name) const {
   std::string header;
diff --git a/content/browser/appcache/appcache_request.h b/content/browser/appcache/appcache_request.h
index 244ab605..cf189e4 100644
--- a/content/browser/appcache/appcache_request.h
+++ b/content/browser/appcache/appcache_request.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "content/common/content_export.h"
 #include "services/network/public/cpp/resource_request.h"
@@ -23,6 +24,10 @@
 struct ResourceRequest;
 }
 
+namespace url {
+class Origin;
+}
+
 namespace content {
 
 // Interface for an AppCache request. Subclasses implement this interface to
@@ -44,6 +49,9 @@
     return request_.site_for_cookies;
   }
 
+  // Used for cookie policy.
+  base::Optional<url::Origin> GetTopFrameOrigin() const;
+
   // The referrer for this request.
   const GURL GetReferrer() const { return request_.referrer; }
 
diff --git a/content/browser/appcache/appcache_request_handler.cc b/content/browser/appcache/appcache_request_handler.cc
index b4f3319..9346278e 100644
--- a/content/browser/appcache/appcache_request_handler.cc
+++ b/content/browser/appcache/appcache_request_handler.cc
@@ -360,7 +360,8 @@
   bool was_blocked_by_policy =
       !manifest_url.is_empty() && policy &&
       !policy->CanLoadAppCache(manifest_url,
-                               host_->site_for_cookies().RepresentativeUrl());
+                               host_->site_for_cookies().RepresentativeUrl(),
+                               host_->top_frame_origin());
 
   if (was_blocked_by_policy) {
     if (blink::IsResourceTypeFrame(resource_type_)) {
diff --git a/content/browser/appcache/chrome_appcache_service.cc b/content/browser/appcache/chrome_appcache_service.cc
index 9eb2d4e..c098e0d 100644
--- a/content/browser/appcache/chrome_appcache_service.cc
+++ b/content/browser/appcache/chrome_appcache_service.cc
@@ -56,18 +56,22 @@
   partition_ = nullptr;
 }
 
-bool ChromeAppCacheService::CanLoadAppCache(const GURL& manifest_url,
-                                            const GURL& first_party) {
+bool ChromeAppCacheService::CanLoadAppCache(
+    const GURL& manifest_url,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& top_frame_origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return GetContentClient()->browser()->AllowAppCache(manifest_url, first_party,
-                                                      browser_context_);
+  return GetContentClient()->browser()->AllowAppCache(
+      manifest_url, site_for_cookies, top_frame_origin, browser_context_);
 }
 
 bool ChromeAppCacheService::CanCreateAppCache(
-    const GURL& manifest_url, const GURL& first_party) {
+    const GURL& manifest_url,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& top_frame_origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return GetContentClient()->browser()->AllowAppCache(manifest_url, first_party,
-                                                      browser_context_);
+  return GetContentClient()->browser()->AllowAppCache(
+      manifest_url, site_for_cookies, top_frame_origin, browser_context_);
 }
 
 bool ChromeAppCacheService::IsOriginTrialRequiredForAppCache() {
diff --git a/content/browser/appcache/chrome_appcache_service.h b/content/browser/appcache/chrome_appcache_service.h
index 58e7d8aa..731abba 100644
--- a/content/browser/appcache/chrome_appcache_service.h
+++ b/content/browser/appcache/chrome_appcache_service.h
@@ -64,10 +64,14 @@
   void Shutdown();
 
   // AppCachePolicy overrides
-  bool CanLoadAppCache(const GURL& manifest_url,
-                       const GURL& first_party) override;
-  bool CanCreateAppCache(const GURL& manifest_url,
-                         const GURL& first_party) override;
+  bool CanLoadAppCache(
+      const GURL& manifest_url,
+      const GURL& site_for_cookies,
+      const base::Optional<url::Origin>& top_frame_origin) override;
+  bool CanCreateAppCache(
+      const GURL& manifest_url,
+      const GURL& site_for_cookies,
+      const base::Optional<url::Origin>& top_frame_origin) override;
   bool IsOriginTrialRequiredForAppCache() override;
 
  protected:
diff --git a/content/browser/appcache/mock_appcache_policy.cc b/content/browser/appcache/mock_appcache_policy.cc
index 5e6a332..c9fbcfa 100644
--- a/content/browser/appcache/mock_appcache_policy.cc
+++ b/content/browser/appcache/mock_appcache_policy.cc
@@ -15,14 +15,18 @@
 
 MockAppCachePolicy::~MockAppCachePolicy() = default;
 
-bool MockAppCachePolicy::CanLoadAppCache(const GURL& manifest_url,
-                                         const GURL& first_party) {
+bool MockAppCachePolicy::CanLoadAppCache(
+    const GURL& manifest_url,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& top_frame_origin) {
   requested_manifest_url_ = manifest_url;
   return can_load_return_value_;
 }
 
-bool MockAppCachePolicy::CanCreateAppCache(const GURL& manifest_url,
-                                           const GURL& first_party) {
+bool MockAppCachePolicy::CanCreateAppCache(
+    const GURL& manifest_url,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& top_frame_origin) {
   requested_manifest_url_ = manifest_url;
   return can_create_return_value_;
 }
diff --git a/content/browser/appcache/mock_appcache_policy.h b/content/browser/appcache/mock_appcache_policy.h
index d0704c5..747315e 100644
--- a/content/browser/appcache/mock_appcache_policy.h
+++ b/content/browser/appcache/mock_appcache_policy.h
@@ -16,10 +16,14 @@
   MockAppCachePolicy();
   virtual ~MockAppCachePolicy();
 
-  bool CanLoadAppCache(const GURL& manifest_url,
-                       const GURL& first_party) override;
-  bool CanCreateAppCache(const GURL& manifest_url,
-                         const GURL& first_party) override;
+  bool CanLoadAppCache(
+      const GURL& manifest_url,
+      const GURL& site_for_cookies,
+      const base::Optional<url::Origin>& top_frame_origin) override;
+  bool CanCreateAppCache(
+      const GURL& manifest_url,
+      const GURL& site_for_cookies,
+      const base::Optional<url::Origin>& top_frame_origin) override;
   bool IsOriginTrialRequiredForAppCache() override;
 
   bool can_load_return_value_;
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 699e45f..e7009984 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -411,7 +411,6 @@
     ServiceProcessHost::Launch(
         std::move(receiver),
         ServiceProcessHost::Options()
-            .WithSandboxType(service_manager::SandboxType::kUtility)
             .WithDisplayName("Data Decoder Service")
             .Pass());
   }
diff --git a/content/browser/cross_origin_opener_policy_browsertest.cc b/content/browser/cross_origin_opener_policy_browsertest.cc
index 95185ffbb..6fc47dc1 100644
--- a/content/browser/cross_origin_opener_policy_browsertest.cc
+++ b/content/browser/cross_origin_opener_policy_browsertest.cc
@@ -47,6 +47,7 @@
     std::vector<base::Feature> features;
     feature_list_.InitWithFeatures(
         {network::features::kCrossOriginOpenerPolicy,
+         network::features::kCrossOriginOpenerPolicyReporting,
          network::features::kCrossOriginEmbedderPolicy},
         {});
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
@@ -331,9 +332,7 @@
   EXPECT_EQ(iframe_rfh->GetLastCommittedURL(), iframe_navigation_url);
   EXPECT_EQ(iframe_rfh->GetSiteInstance(), non_coop_iframe_site_instance);
 
-  // TODO(pmeuleman, ahemery): Don't store COOP on subframes as it will not be
-  // used anyway.
-  EXPECT_EQ(iframe_rfh->cross_origin_opener_policy(), CoopSameOrigin());
+  EXPECT_EQ(iframe_rfh->cross_origin_opener_policy(), CoopUnsafeNone());
 }
 
 IN_PROC_BROWSER_TEST_F(CrossOriginOpenerPolicyBrowserTest,
diff --git a/content/browser/devtools/BUILD.gn b/content/browser/devtools/BUILD.gn
index 2cd01cd..d3670fb 100644
--- a/content/browser/devtools/BUILD.gn
+++ b/content/browser/devtools/BUILD.gn
@@ -82,6 +82,8 @@
     "protocol/inspector.h",
     "protocol/io.cc",
     "protocol/io.h",
+    "protocol/log.cc",
+    "protocol/log.h",
     "protocol/memory.cc",
     "protocol/memory.h",
     "protocol/network.cc",
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index df1f100..81f3c5a0 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -3,12 +3,14 @@
 // found in the LICENSE file.
 #include "content/browser/devtools/devtools_instrumentation.h"
 
+#include "base/strings/stringprintf.h"
 #include "components/download/public/common/download_create_info.h"
 #include "components/download/public/common/download_item.h"
 #include "content/browser/devtools/browser_devtools_agent_host.h"
 #include "content/browser/devtools/devtools_url_loader_interceptor.h"
 #include "content/browser/devtools/protocol/emulation_handler.h"
 #include "content/browser/devtools/protocol/fetch_handler.h"
+#include "content/browser/devtools/protocol/log_handler.h"
 #include "content/browser/devtools/protocol/network_handler.h"
 #include "content/browser/devtools/protocol/page_handler.h"
 #include "content/browser/devtools/protocol/security_handler.h"
@@ -729,6 +731,22 @@
           std::move(details)));
 }
 
+void OnQuicTransportHandshakeFailed(RenderFrameHostImpl* frame,
+                                    const GURL& url) {
+  FrameTreeNode* ftn = frame->frame_tree_node();
+  if (!ftn)
+    return;
+  std::string text = base::StringPrintf(
+      "Failed to establish a connection to %s.", url.spec().c_str());
+  auto entry = protocol::Log::LogEntry::Create()
+                   .SetSource(protocol::Log::LogEntry::SourceEnum::Network)
+                   .SetLevel(protocol::Log::LogEntry::LevelEnum::Error)
+                   .SetText(text)
+                   .SetTimestamp(base::Time::Now().ToDoubleT() * 1000.0)
+                   .Build();
+  DispatchToAgents(ftn, &protocol::LogHandler::EntryAdded, std::move(entry));
+}
+
 }  // namespace devtools_instrumentation
 
 }  // namespace content
diff --git a/content/browser/devtools/devtools_instrumentation.h b/content/browser/devtools/devtools_instrumentation.h
index cbe5874..eb975dae 100644
--- a/content/browser/devtools/devtools_instrumentation.h
+++ b/content/browser/devtools/devtools_instrumentation.h
@@ -178,6 +178,10 @@
     const net::SiteForCookies& site_for_cookies,
     blink::mojom::SameSiteCookieOperation operation,
     const base::Optional<std::string>& devtools_request_id);
+
+void OnQuicTransportHandshakeFailed(RenderFrameHostImpl* frame_host,
+                                    const GURL& url);
+
 }  // namespace devtools_instrumentation
 
 }  // namespace content
diff --git a/content/browser/devtools/protocol/log_handler.cc b/content/browser/devtools/protocol/log_handler.cc
new file mode 100644
index 0000000..4fca4dd
--- /dev/null
+++ b/content/browser/devtools/protocol/log_handler.cc
@@ -0,0 +1,43 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/devtools/protocol/log_handler.h"
+
+#include "content/browser/devtools/devtools_agent_host_impl.h"
+
+namespace content {
+namespace protocol {
+
+LogHandler::LogHandler() : DevToolsDomainHandler(Log::Metainfo::domainName) {}
+LogHandler::~LogHandler() = default;
+
+// static
+std::vector<LogHandler*> LogHandler::ForAgentHost(DevToolsAgentHostImpl* host) {
+  return host->HandlersByName<LogHandler>(Log::Metainfo::domainName);
+}
+
+void LogHandler::Wire(UberDispatcher* dispatcher) {
+  frontend_ = std::make_unique<Log::Frontend>(dispatcher->channel());
+  Log::Dispatcher::wire(dispatcher, this);
+}
+
+DispatchResponse LogHandler::Disable() {
+  enabled_ = false;
+  return Response::FallThrough();
+}
+
+DispatchResponse LogHandler::Enable() {
+  enabled_ = true;
+  return Response::FallThrough();
+}
+
+void LogHandler::EntryAdded(std::unique_ptr<Log::LogEntry> entry) {
+  if (!enabled_) {
+    return;
+  }
+  frontend_->EntryAdded(std::move(entry));
+}
+
+}  // namespace protocol
+}  // namespace content
\ No newline at end of file
diff --git a/content/browser/devtools/protocol/log_handler.h b/content/browser/devtools/protocol/log_handler.h
new file mode 100644
index 0000000..45b5455
--- /dev/null
+++ b/content/browser/devtools/protocol/log_handler.h
@@ -0,0 +1,46 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DEVTOOLS_PROTOCOL_LOG_HANDLER_H_
+#define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_LOG_HANDLER_H_
+
+#include "base/macros.h"
+#include "content/browser/devtools/protocol/devtools_domain_handler.h"
+#include "content/browser/devtools/protocol/log.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/mojom/leak_detector/leak_detector.mojom.h"
+
+namespace content {
+
+class DevToolsAgentHostImpl;
+
+namespace protocol {
+
+class LogHandler final : public DevToolsDomainHandler, public Log::Backend {
+ public:
+  LogHandler();
+  ~LogHandler() override;
+
+  static std::vector<LogHandler*> ForAgentHost(DevToolsAgentHostImpl* host);
+
+  // DevToolsDomainHandler implementation.
+  void Wire(UberDispatcher* dispatcher) override;
+
+  // Log::Backend implementation.
+  DispatchResponse Disable() override;
+  DispatchResponse Enable() override;
+
+  void EntryAdded(std::unique_ptr<Log::LogEntry> entry);
+
+ private:
+  std::unique_ptr<Log::Frontend> frontend_;
+  bool enabled_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(LogHandler);
+};
+
+}  // namespace protocol
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_DEVTOOLS_PROTOCOL_LOG_HANDLER_H_
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index 3c65670a..604cebf 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -39,6 +39,10 @@
                 "exclude": ["resolveBlob"]
             },
             {
+                "domain": "Log",
+                "include": ["LogEntry", "entryAdded", "enable", "disable"]
+            },
+            {
                 "domain": "Memory",
                 "include": ["getBrowserSamplingProfile", "setPressureNotificationsSuppressed", "simulatePressureNotification", "prepareForLeakDetection"],
                 "async": ["prepareForLeakDetection"]
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 27ecb9a..d70c3971 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -29,6 +29,7 @@
 #include "content/browser/devtools/protocol/input_handler.h"
 #include "content/browser/devtools/protocol/inspector_handler.h"
 #include "content/browser/devtools/protocol/io_handler.h"
+#include "content/browser/devtools/protocol/log_handler.h"
 #include "content/browser/devtools/protocol/memory_handler.h"
 #include "content/browser/devtools/protocol/network_handler.h"
 #include "content/browser/devtools/protocol/overlay_handler.h"
@@ -106,8 +107,8 @@
 }  // namespace
 
 // static
-scoped_refptr<DevToolsAgentHost>
-DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) {
+scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetOrCreateFor(
+    WebContents* web_contents) {
   FrameTreeNode* node =
       static_cast<WebContentsImpl*>(web_contents)->GetFrameTree()->root();
   // TODO(dgozman): this check should not be necessary. See
@@ -340,6 +341,7 @@
     session->AddHandler(std::make_unique<protocol::TracingHandler>(
         frame_tree_node_, GetIOContext()));
   }
+  session->AddHandler(std::make_unique<protocol::LogHandler>());
 #if !defined(OS_ANDROID)
   session->AddHandler(std::make_unique<protocol::WebAuthnHandler>());
 #endif  // !defined(OS_ANDROID)
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 8e11d6e..05ac3a6 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -2002,8 +2002,8 @@
   if (base::FeatureList::IsEnabled(
           network::features::kCrossOriginOpenerPolicy)) {
     // The Cross-Origin-Opener-Policy header should be ignored if delivered in
-    // insecure contexts.
-    if (!IsOriginSecure(common_params_->url)) {
+    // insecure contexts, and non-top level documents.
+    if (!IsOriginSecure(common_params_->url) || !IsInMainFrame()) {
       response_head_->parsed_headers->cross_origin_opener_policy =
           network::CrossOriginOpenerPolicy();
     }
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 369edfa..ca5e9b2 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -6970,10 +6970,11 @@
 
 void RenderFrameHostImpl::CreateQuicTransportConnector(
     mojo::PendingReceiver<blink::mojom::QuicTransportConnector> receiver) {
-  mojo::MakeSelfOwnedReceiver(std::make_unique<QuicTransportConnectorImpl>(
-                                  GetProcess()->GetID(), last_committed_origin_,
-                                  isolation_info_.network_isolation_key()),
-                              std::move(receiver));
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<QuicTransportConnectorImpl>(
+          GetProcess()->GetID(), weak_ptr_factory_.GetWeakPtr(),
+          last_committed_origin_, isolation_info_.network_isolation_key()),
+      std::move(receiver));
 }
 
 void RenderFrameHostImpl::CreateNotificationService(
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 1efb02e..5a18dd2 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -46,10 +46,10 @@
   if (!process)
     return;
 
-  mojo::MakeSelfOwnedReceiver(
-      std::make_unique<QuicTransportConnectorImpl>(
-          process_id, origin, net::NetworkIsolationKey(origin, origin)),
-      std::move(receiver));
+  mojo::MakeSelfOwnedReceiver(std::make_unique<QuicTransportConnectorImpl>(
+                                  process_id, /*frame=*/nullptr, origin,
+                                  net::NetworkIsolationKey(origin, origin)),
+                              std::move(receiver));
 }
 
 }  // anonymous namespace
diff --git a/content/browser/tracing/tracing_service_controller.cc b/content/browser/tracing/tracing_service_controller.cc
index 8ce3ade..a7646b2f 100644
--- a/content/browser/tracing/tracing_service_controller.cc
+++ b/content/browser/tracing/tracing_service_controller.cc
@@ -88,7 +88,6 @@
       ServiceProcessHost::Launch(
           std::move(receiver),
           ServiceProcessHost::Options()
-              .WithSandboxType(service_manager::SandboxType::kUtility)
               .WithDisplayName("Tracing Service")
               .Pass());
     }
diff --git a/content/browser/webtransport/quic_transport_connector_impl.cc b/content/browser/webtransport/quic_transport_connector_impl.cc
index 0acddc5..8602ccd 100644
--- a/content/browser/webtransport/quic_transport_connector_impl.cc
+++ b/content/browser/webtransport/quic_transport_connector_impl.cc
@@ -3,19 +3,61 @@
 // found in the LICENSE file.
 
 #include "content/browser/webtransport/quic_transport_connector_impl.h"
+#include "content/browser/devtools/devtools_instrumentation.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/storage_partition.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 namespace content {
 
+namespace {
+
+using network::mojom::QuicTransportHandshakeClient;
+
+class InterceptingHandshakeClient final : public QuicTransportHandshakeClient {
+ public:
+  InterceptingHandshakeClient(
+      base::WeakPtr<RenderFrameHostImpl> frame,
+      const GURL& url,
+      mojo::PendingRemote<QuicTransportHandshakeClient> remote)
+      : frame_(std::move(frame)), url_(url), remote_(std::move(remote)) {}
+  ~InterceptingHandshakeClient() override = default;
+
+  // QuicTransportHandshakeClient implementation:
+  void OnConnectionEstablished(
+      mojo::PendingRemote<network::mojom::QuicTransport> transport,
+      mojo::PendingReceiver<network::mojom::QuicTransportClient> client)
+      override {
+    remote_->OnConnectionEstablished(std::move(transport), std::move(client));
+  }
+  void OnHandshakeFailed() override {
+    remote_->OnHandshakeFailed();
+
+    if (RenderFrameHostImpl* frame = frame_.get()) {
+      devtools_instrumentation::OnQuicTransportHandshakeFailed(frame, url_);
+    }
+  }
+
+ private:
+  const base::WeakPtr<RenderFrameHostImpl> frame_;
+  const GURL url_;
+  mojo::Remote<QuicTransportHandshakeClient> remote_;
+};
+
+}  // namespace
+
 QuicTransportConnectorImpl::QuicTransportConnectorImpl(
     int process_id,
+    base::WeakPtr<RenderFrameHostImpl> frame,
     const url::Origin& origin,
     const net::NetworkIsolationKey& network_isolation_key)
     : process_id_(process_id),
+      frame_(std::move(frame)),
       origin_(origin),
       network_isolation_key_(network_isolation_key) {}
 
+QuicTransportConnectorImpl::~QuicTransportConnectorImpl() = default;
+
 void QuicTransportConnectorImpl::Connect(
     const GURL& url,
     mojo::PendingRemote<network::mojom::QuicTransportHandshakeClient>
@@ -24,8 +66,19 @@
   if (!process) {
     return;
   }
+
+  mojo::PendingRemote<QuicTransportHandshakeClient> handshake_client_to_pass;
+  // TODO(yhirano): Stop using MakeSelfOwnedReceiver here, because the
+  // QuicTransport implementation in the network service won't notice that
+  // the QuicTransportHandshakeClient is going away.
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<InterceptingHandshakeClient>(
+          frame_, url, std::move(handshake_client)),
+      handshake_client_to_pass.InitWithNewPipeAndPassReceiver());
+
   process->GetStoragePartition()->GetNetworkContext()->CreateQuicTransport(
-      url, origin_, network_isolation_key_, std::move(handshake_client));
+      url, origin_, network_isolation_key_,
+      std::move(handshake_client_to_pass));
 }
 
 }  // namespace content
diff --git a/content/browser/webtransport/quic_transport_connector_impl.h b/content/browser/webtransport/quic_transport_connector_impl.h
index b7326128..cd5a8c0 100644
--- a/content/browser/webtransport/quic_transport_connector_impl.h
+++ b/content/browser/webtransport/quic_transport_connector_impl.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_BROWSER_WEBTRANSPORT_QUIC_TRANSPORT_CONNECTOR_IMPL_H_
 #define CONTENT_BROWSER_WEBTRANSPORT_QUIC_TRANSPORT_CONNECTOR_IMPL_H_
 
+#include "base/memory/weak_ptr.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/network_isolation_key.h"
 #include "services/network/public/mojom/network_context.mojom.h"
@@ -14,13 +15,20 @@
 
 namespace content {
 
+class RenderFrameHostImpl;
+
 class QuicTransportConnectorImpl final
     : public blink::mojom::QuicTransportConnector {
  public:
+  // |frame| is needed for devtools - sometimes (e.g., the connector is for
+  // workers) there is not appropriate frame to associate, and in that case
+  // nullptr is provided.
   QuicTransportConnectorImpl(
       int process_id,
+      base::WeakPtr<RenderFrameHostImpl> frame,
       const url::Origin& origin,
       const net::NetworkIsolationKey& network_isolation_key);
+  ~QuicTransportConnectorImpl() override;
 
   void Connect(const GURL& url,
                mojo::PendingRemote<network::mojom::QuicTransportHandshakeClient>
@@ -28,6 +36,7 @@
 
  private:
   const int process_id_;
+  const base::WeakPtr<RenderFrameHostImpl> frame_;
   const url::Origin origin_;
   const net::NetworkIsolationKey network_isolation_key_;
 };
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index fab014b4..179dbf0b 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -385,10 +385,11 @@
     // will soon be terminated too, so abort the connection.
     return;
   }
-  mojo::MakeSelfOwnedReceiver(std::make_unique<QuicTransportConnectorImpl>(
-                                  worker_process_host_->GetID(), worker_origin_,
-                                  isolation_info_.network_isolation_key()),
-                              std::move(receiver));
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<QuicTransportConnectorImpl>(
+          worker_process_host_->GetID(), /*frame=*/nullptr, worker_origin_,
+          isolation_info_.network_isolation_key()),
+      std::move(receiver));
 }
 
 void DedicatedWorkerHost::CreateWakeLockService(
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index ab6078e..5350ebb 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -339,10 +339,11 @@
     mojo::PendingReceiver<blink::mojom::QuicTransportConnector> receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   const url::Origin origin = url::Origin::Create(instance().url());
-  mojo::MakeSelfOwnedReceiver(std::make_unique<QuicTransportConnectorImpl>(
-                                  worker_process_host_->GetID(), origin,
-                                  net::NetworkIsolationKey(origin, origin)),
-                              std::move(receiver));
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<QuicTransportConnectorImpl>(
+          worker_process_host_->GetID(), /*frame=*/nullptr, origin,
+          net::NetworkIsolationKey(origin, origin)),
+      std::move(receiver));
 }
 
 void SharedWorkerHost::BindCacheStorage(
diff --git a/content/browser/xr/service/xr_device_service.cc b/content/browser/xr/service/xr_device_service.cc
index 2947351..5c70413 100644
--- a/content/browser/xr/service/xr_device_service.cc
+++ b/content/browser/xr/service/xr_device_service.cc
@@ -28,8 +28,6 @@
         content::ServiceProcessHost::Options()
 #if defined(OS_WIN)
             .WithSandboxType(service_manager::SandboxType::kXrCompositing)
-#else
-            .WithSandboxType(service_manager::SandboxType::kUtility)
 #endif
             .WithDisplayName("Isolated XR Device Service")
             .Pass());
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index a3743fc..37ada4e8 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -305,9 +305,11 @@
   return base::FilePath();
 }
 
-bool ContentBrowserClient::AllowAppCache(const GURL& manifest_url,
-                                         const GURL& first_party,
-                                         BrowserContext* context) {
+bool ContentBrowserClient::AllowAppCache(
+    const GURL& manifest_url,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& top_frame_origin,
+    BrowserContext* context) {
   return true;
 }
 
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 57b2172..cd07ed98 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -581,9 +581,11 @@
 
   // Allow the embedder to control if an AppCache can be used for the given url.
   // This is called on the UI thread.
-  virtual bool AllowAppCache(const GURL& manifest_url,
-                             const GURL& first_party,
-                             BrowserContext* context);
+  virtual bool AllowAppCache(
+      const GURL& manifest_url,
+      const GURL& site_for_cookies,
+      const base::Optional<url::Origin>& top_frame_origin,
+      BrowserContext* context);
 
   // Allows the embedder to control if a service worker is allowed at the given
   // |scope| and can be accessed from |site_for_cookies| and |top_frame_origin|.
diff --git a/content/public/browser/service_process_host.h b/content/public/browser/service_process_host.h
index d051a79..acd93c4 100644
--- a/content/public/browser/service_process_host.h
+++ b/content/public/browser/service_process_host.h
@@ -35,7 +35,6 @@
 //   constexpr auto kFooServiceIdleTimeout = base::TimeDelta::FromSeconds(5);
 //   auto foo_service = ServiceProcessHost::Launch<foo::mojom::FooService>(
 //       ServiceProcessHost::Options()
-//           .WithSandboxType(SandboxType::kUtility)
 //           .WithDisplayName(IDS_FOO_SERVICE_DISPLAY_NAME)
 //           .Pass());
 //   foo_service.set_idle_handler(
diff --git a/content/public/common/content_switch_dependent_feature_overrides.cc b/content/public/common/content_switch_dependent_feature_overrides.cc
index 47cddab..27df27b 100644
--- a/content/public/common/content_switch_dependent_feature_overrides.cc
+++ b/content/public/common/content_switch_dependent_feature_overrides.cc
@@ -36,6 +36,9 @@
        std::cref(network::features::kCrossOriginOpenerPolicy),
        base::FeatureList::OVERRIDE_ENABLE_FEATURE},
       {switches::kEnableExperimentalWebPlatformFeatures,
+       std::cref(network::features::kCrossOriginOpenerPolicyReporting),
+       base::FeatureList::OVERRIDE_ENABLE_FEATURE},
+      {switches::kEnableExperimentalWebPlatformFeatures,
        std::cref(network::features::kCrossOriginEmbedderPolicy),
        base::FeatureList::OVERRIDE_ENABLE_FEATURE},
       {switches::kEnableExperimentalWebPlatformFeatures,
diff --git a/content/test/data/accessibility/event/aria-hidden-changed-expected-uia-win.txt b/content/test/data/accessibility/event/aria-hidden-changed-expected-uia-win.txt
index c6206d6..073c1f8 100644
--- a/content/test/data/accessibility/event/aria-hidden-changed-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/aria-hidden-changed-expected-uia-win.txt
@@ -1,3 +1,3 @@
-AriaProperties changed on role=document
 AriaProperties changed on role=heading, name=Item2
+AriaProperties changed on role=heading, name=Item3
 AriaProperties changed on role=heading, name=Item4
diff --git a/content/test/data/accessibility/event/aria-selected-changed-expected-uia-win.txt b/content/test/data/accessibility/event/aria-selected-changed-expected-uia-win.txt
index 1bb067b..432ded8 100644
--- a/content/test/data/accessibility/event/aria-selected-changed-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/aria-selected-changed-expected-uia-win.txt
@@ -13,7 +13,7 @@
 SelectionItem_ElementSelected on role=gridcell, name=Grid4, GridCell2
 === Start Continuation ===
 === Start Continuation ===
-AriaProperties changed on role=combobox
+AriaProperties changed on role=listitem, name=Select2, Option1
 AriaProperties changed on role=listitem, name=Select2, Option2
 SelectionItem_ElementSelected on role=listitem, name=Select2, Option2
 ValueValue changed on role=combobox
diff --git a/content/test/data/accessibility/event/menulist-collapse-expected-uia-win.txt b/content/test/data/accessibility/event/menulist-collapse-expected-uia-win.txt
index 6587810..3b9c78b 100644
--- a/content/test/data/accessibility/event/menulist-collapse-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/menulist-collapse-expected-uia-win.txt
@@ -1,6 +1,6 @@
 AutomationFocusChanged on role=combobox
 AutomationFocusChanged on role=combobox
 === Start Continuation ===
-AriaProperties changed on role=combobox
-SelectionItem_ElementRemovedFromSelection on role=combobox
+AriaProperties changed on role=listitem, name=Apple
+SelectionItem_ElementRemovedFromSelection on role=listitem, name=Apple
 ValueValue changed on role=combobox
diff --git a/content/test/data/accessibility/event/menulist-collapse-expected-uia-win7.txt b/content/test/data/accessibility/event/menulist-collapse-expected-uia-win7.txt
index 6587810..5309132 100644
--- a/content/test/data/accessibility/event/menulist-collapse-expected-uia-win7.txt
+++ b/content/test/data/accessibility/event/menulist-collapse-expected-uia-win7.txt
@@ -1,6 +1,7 @@
 AutomationFocusChanged on role=combobox
 AutomationFocusChanged on role=combobox
 === Start Continuation ===
-AriaProperties changed on role=combobox
-SelectionItem_ElementRemovedFromSelection on role=combobox
+AriaProperties changed on role=listitem, name=Apple
+AutomationFocusChanged on role=combobox
+SelectionItem_ElementRemovedFromSelection on role=listitem, name=Apple
 ValueValue changed on role=combobox
diff --git a/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win.txt b/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win.txt
index 440bad2..c7276f4 100644
--- a/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win.txt
@@ -1,7 +1,7 @@
 AutomationFocusChanged on role=combobox
 AutomationFocusChanged on role=combobox
 === Start Continuation ===
-AriaProperties changed on role=combobox
+AriaProperties changed on role=listitem, name=Apple
 AriaProperties changed on role=listitem, name=Orange
 SelectionItem_ElementSelected on role=listitem, name=Orange
 ValueValue changed on role=combobox
diff --git a/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win7.txt b/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win7.txt
index 098dd7d..5b89bc5 100644
--- a/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win7.txt
+++ b/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win7.txt
@@ -1,7 +1,7 @@
 AutomationFocusChanged on role=combobox
 AutomationFocusChanged on role=combobox
 === Start Continuation ===
-AriaProperties changed on role=combobox
+AriaProperties changed on role=listitem, name=Apple
 AriaProperties changed on role=listitem, name=Orange
 AutomationFocusChanged on role=combobox
 SelectionItem_ElementSelected on role=listitem, name=Orange
diff --git a/content/test/data/accessibility/event/visibility-hidden-changed-expected-uia-win.txt b/content/test/data/accessibility/event/visibility-hidden-changed-expected-uia-win.txt
index b9b6f51d1..d5810d5 100644
--- a/content/test/data/accessibility/event/visibility-hidden-changed-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/visibility-hidden-changed-expected-uia-win.txt
@@ -1,4 +1,4 @@
-AriaProperties changed on role=document
-AriaProperties changed on role=document
+AriaProperties changed on role=heading, name=Item1
 AriaProperties changed on role=heading, name=Item2
+AriaProperties changed on role=heading, name=Item3
 AriaProperties changed on role=heading, name=Item4
diff --git a/content/test/data/accessibility/event/visibility-hidden-changed-expected-uia-win7.txt b/content/test/data/accessibility/event/visibility-hidden-changed-expected-uia-win7.txt
deleted file mode 100644
index c6206d6..0000000
--- a/content/test/data/accessibility/event/visibility-hidden-changed-expected-uia-win7.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-AriaProperties changed on role=document
-AriaProperties changed on role=heading, name=Item2
-AriaProperties changed on role=heading, name=Item4
diff --git a/content/test/data/accessibility/html/canvas-fallback-expected-uia-win.txt b/content/test/data/accessibility/html/canvas-fallback-expected-uia-win.txt
index 5b2328f..9588e63 100644
--- a/content/test/data/accessibility/html/canvas-fallback-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/canvas-fallback-expected-uia-win.txt
@@ -10,7 +10,7 @@
 ++++++Group Name='Visibility hidden paragraph in fallback content'
 ++++++++Text Name='This is another paragraph in fallback'
 ++++++Text Name='<newline>    '
-++++++Text Name='Aria hidden paragraph in fallback content' IsControlElement=false
+++++++Text Name='Aria hidden paragraph in fallback content'
 ++++++++Text Name='Aria hidden paragraph in fallback content' IsControlElement=false
 ++++++Text Name='<newline>    '
 ++++++Text IsControlElement=false
diff --git a/fuchsia/engine/web_engine_debug_integration_test.cc b/fuchsia/engine/web_engine_debug_integration_test.cc
index c474c24..3dd1fe35 100644
--- a/fuchsia/engine/web_engine_debug_integration_test.cc
+++ b/fuchsia/engine/web_engine_debug_integration_test.cc
@@ -52,7 +52,13 @@
     web_context_provider_.set_error_handler(
         [](zx_status_t status) { ADD_FAILURE(); });
 
-    WaitForWebEngine();
+    // Wait for the OnDirectoryReady event, which indicates that the component's
+    // outgoing directory is available, including the "/debug" contents accessed
+    // via the Hub.
+    base::RunLoop directory_loop;
+    web_engine_controller_.events().OnDirectoryReady =
+        [quit_loop = directory_loop.QuitClosure()]() { quit_loop.Run(); };
+    directory_loop.Run();
 
     // Enumerate all entries in /hub/c/context_provider.cmx to find WebEngine
     // instance with |test_arg|.
@@ -96,36 +102,6 @@
   }
 
  protected:
-  void WaitForWebEngine() {
-    // Create a throwaway web context to ensure the WebEngine process is
-    // initialized and a Debug instance can be created. This is necessary
-    // because the Debug service is not available on the debug directory until
-    // after the WebEngine is fully initialized.
-    fuchsia::web::CreateContextParams create_params;
-    auto directory = base::fuchsia::OpenDirectory(
-        base::FilePath(base::fuchsia::kServiceDirectoryPath));
-    ASSERT_TRUE(directory.is_valid());
-    create_params.set_service_directory(std::move(directory));
-
-    fuchsia::web::ContextPtr web_context;
-    web_context_provider_->Create(std::move(create_params),
-                                  web_context.NewRequest());
-    web_context.set_error_handler([](zx_status_t status) { ADD_FAILURE(); });
-
-    base::RunLoop run_loop;
-    cr_fuchsia::ResultReceiver<
-        fuchsia::web::Context_GetRemoteDebuggingPort_Result>
-        port_receiver(run_loop.QuitClosure());
-    web_context->GetRemoteDebuggingPort(
-        cr_fuchsia::CallbackToFitFunction(port_receiver.GetReceiveCallback()));
-    run_loop.Run();
-
-    // Sanity check.
-    ASSERT_TRUE(port_receiver->is_err());
-    ASSERT_EQ(port_receiver->err(),
-              fuchsia::web::ContextError::REMOTE_DEBUGGING_PORT_NOT_OPENED);
-  }
-
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
 
@@ -133,8 +109,7 @@
   fidl::Binding<fuchsia::web::DevToolsListener> dev_tools_listener_binding_;
   std::unique_ptr<sys::ServiceDirectory> debug_dir_;
   fuchsia::web::ContextProviderPtr web_context_provider_;
-  fidl::InterfaceHandle<fuchsia::sys::ComponentController>
-      web_engine_controller_;
+  fuchsia::sys::ComponentControllerPtr web_engine_controller_;
   fuchsia::web::DebugSyncPtr debug_;
 
   base::OnceClosure on_url_fetch_complete_ack_;
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index 6c69a00b..3b87b7e 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -813,7 +813,12 @@
       >
       builders: <
         name: "chromium/try/linux-perfetto-rel"
-        includable_only: true
+        experiment_percentage: 100
+        location_regexp: ".+/[+]/base/trace_event/.+"
+        location_regexp: ".+/[+]/base/tracing/.+"
+        location_regexp: ".+/[+]/components/tracing/.+"
+        location_regexp: ".+/[+]/content/browser/tracing/.+"
+        location_regexp: ".+/[+]/services/tracing/.+"
       >
       builders: <
         name: "chromium/try/linux-rel"
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index 8e797e6..cd9adb9e 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -339,6 +339,16 @@
 * [fuchsia-compile-x64-dbg](https://ci.chromium.org/p/chromium/builders/try/fuchsia-compile-x64-dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+fuchsia-compile-x64-dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+fuchsia-compile-x64-dbg))
   * Experiment percentage: 50
 
+* [linux-perfetto-rel](https://ci.chromium.org/p/chromium/builders/try/linux-perfetto-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+linux-perfetto-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+linux-perfetto-rel))
+  * Experiment percentage: 100
+
+  Path regular expressions:
+  * [`//base/trace_event/.+`](https://cs.chromium.org/chromium/src/base/trace_event/)
+  * [`//base/tracing/.+`](https://cs.chromium.org/chromium/src/base/tracing/)
+  * [`//components/tracing/.+`](https://cs.chromium.org/chromium/src/components/tracing/)
+  * [`//content/browser/tracing/.+`](https://cs.chromium.org/chromium/src/content/browser/tracing/)
+  * [`//services/tracing/.+`](https://cs.chromium.org/chromium/src/services/tracing/)
+
 * [mac-coverage-rel](https://ci.chromium.org/p/chromium/builders/try/mac-coverage-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+mac-coverage-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+mac-coverage-rel))
   * Experiment percentage: 3
 
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 3da2cd5..c6a2465 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -377,6 +377,16 @@
 
 try_.chromium_linux_builder(
     name = 'linux-perfetto-rel',
+    tryjob = try_.job(
+        experiment_percentage = 100,
+        location_regexp = [
+            '.+/[+]/base/trace_event/.+',
+            '.+/[+]/base/tracing/.+',
+            '.+/[+]/components/tracing/.+',
+            '.+/[+]/content/browser/tracing/.+',
+            '.+/[+]/services/tracing/.+',
+        ],
+    ),
 )
 
 try_.chromium_linux_builder(
diff --git a/ios/build/chrome_build.gni b/ios/build/chrome_build.gni
index e1e42cf..95b3cbf 100644
--- a/ios/build/chrome_build.gni
+++ b/ios/build/chrome_build.gni
@@ -21,7 +21,12 @@
   # Overridden when using the Google-internal repository to build Chrome on iOS.
   ios_account_verification_provider_target = "//ios/chrome/common/credential_provider:account_verification_provider_implementation"
 
-  # Enable multi-window configuration in Info.plist
+  # Enable use of SceneDelegate-driven startup flow in
+  # Info.plist.
+  ios_enable_scene_startup = false
+
+  # Enable multi-window configuration in Info.plist. Includes all the effects of
+  # setting |ios_enable_scene_startup| to |true|.
   ios_enable_multi_window = false
 
   # Value of the encryption export compliance code. See "Cryptography and
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 7c87115..0d6f14b 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -91,7 +91,9 @@
     info_plists += [ "resources/EncryptionExportCompliance+Info.plist" ]
   }
   if (ios_enable_multi_window) {
-    info_plists += [ "resources/MultiWindow+Info.plist" ]
+    info_plists += [ "resources/MultiWindowEnabled+Info.plist" ]
+  } else if (ios_enable_scene_startup) {
+    info_plists += [ "resources/MultiWindowDisabled+Info.plist" ]
   }
   args = [
     "--breakpad=$breakpad_enabled_as_int",
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 541534f6..4ca4745 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -159,7 +159,7 @@
     _mainApplicationDelegate = applicationDelegate;
 
     if (@available(iOS 13, *)) {
-      if (IsMultiwindowSupported()) {
+      if (IsSceneStartupSupported()) {
         // Subscribe for scene activation notifications.
         [[NSNotificationCenter defaultCenter]
             addObserver:self
@@ -515,9 +515,9 @@
 }
 
 - (NSArray<SceneState*>*)connectedScenes {
-  if (IsMultiwindowSupported()) {
-    NSMutableArray* sceneStates = [[NSMutableArray alloc] init];
+  if (IsSceneStartupSupported()) {
     if (@available(iOS 13, *)) {
+      NSMutableArray* sceneStates = [[NSMutableArray alloc] init];
       NSSet* connectedScenes =
           [UIApplication sharedApplication].connectedScenes;
       for (UIWindowScene* scene in connectedScenes) {
@@ -530,8 +530,10 @@
             base::mac::ObjCCastStrict<SceneDelegate>(scene.delegate);
         [sceneStates addObject:sceneDelegate.sceneState];
       }
+      return sceneStates;
     }
-    return sceneStates;
+    NOTREACHED();
+    return @[];
   } else {
     return @[ self.mainSceneState ];
   }
@@ -593,7 +595,7 @@
 
 // Handler for UISceneDidActivateNotification.
 - (void)sceneDidActivate:(NSNotification*)notification {
-  DCHECK(IsMultiwindowSupported());
+  DCHECK(IsSceneStartupSupported());
   if (@available(iOS 13, *)) {
     if (!self.firstSceneHasActivated) {
       self.firstSceneHasActivated = YES;
diff --git a/ios/chrome/app/main_application_delegate.mm b/ios/chrome/app/main_application_delegate.mm
index 1da1713..f69b62b5 100644
--- a/ios/chrome/app/main_application_delegate.mm
+++ b/ios/chrome/app/main_application_delegate.mm
@@ -75,10 +75,10 @@
     [_mainController setAppState:_appState];
     [_appState addObserver:_mainController];
 
-    if (!IsMultiwindowSupported()) {
-      // When multiwindow is not supported, this object holds a "scene" state
-      // and a "scene" controller. This allows the rest of the app to be mostly
-      // multiwindow-agnostic.
+    if (!IsSceneStartupSupported()) {
+      // When the UIScene APU is not supported, this object holds a "scene"
+      // state and a "scene" controller. This allows the rest of the app to be
+      // mostly multiwindow-agnostic.
       _sceneState = [[SceneState alloc] init];
       _appState.mainSceneState = _sceneState;
       _sceneController =
@@ -125,12 +125,12 @@
   BOOL requiresHandling =
       [_appState requiresHandlingAfterLaunchWithOptions:launchOptions
                                         stateBackground:inBackground];
-  if (!IsMultiwindowSupported()) {
+  if (!IsSceneStartupSupported()) {
     self.sceneState.activationLevel = SceneActivationLevelForegroundInactive;
   }
 
   if (@available(iOS 13, *)) {
-    if (IsMultiwindowSupported()) {
+    if (IsSceneStartupSupported()) {
       [[NSNotificationCenter defaultCenter]
           addObserver:self
              selector:@selector(sceneWillConnect:)
@@ -148,7 +148,7 @@
 }
 
 - (void)applicationDidBecomeActive:(UIApplication*)application {
-  if (!IsMultiwindowSupported()) {
+  if (!IsSceneStartupSupported()) {
     self.sceneState.activationLevel = SceneActivationLevelForegroundActive;
   }
 
@@ -161,7 +161,7 @@
 }
 
 - (void)applicationWillResignActive:(UIApplication*)application {
-  if (!IsMultiwindowSupported()) {
+  if (!IsSceneStartupSupported()) {
     self.sceneState.activationLevel = SceneActivationLevelForegroundInactive;
   }
 
@@ -174,7 +174,7 @@
 // Called when going into the background. iOS already broadcasts, so
 // stakeholders can register for it directly.
 - (void)applicationDidEnterBackground:(UIApplication*)application {
-  if (!IsMultiwindowSupported()) {
+  if (!IsSceneStartupSupported()) {
     self.sceneState.activationLevel = SceneActivationLevelBackground;
   }
 
@@ -186,7 +186,7 @@
 
 // Called when returning to the foreground.
 - (void)applicationWillEnterForeground:(UIApplication*)application {
-  if (!IsMultiwindowSupported()) {
+  if (!IsSceneStartupSupported()) {
     self.sceneState.activationLevel = SceneActivationLevelForegroundInactive;
   }
 
@@ -215,7 +215,7 @@
 #pragma mark - Scenes lifecycle
 
 - (NSInteger)foregroundSceneCount {
-  DCHECK(IsMultiwindowSupported());
+  DCHECK(IsSceneStartupSupported());
   if (@available(iOS 13, *)) {
     NSInteger foregroundSceneCount = 0;
     for (UIScene* scene in UIApplication.sharedApplication.connectedScenes) {
@@ -230,7 +230,7 @@
 }
 
 - (void)sceneWillConnect:(NSNotification*)notification {
-  DCHECK(IsMultiwindowSupported());
+  DCHECK(IsSceneStartupSupported());
   if (@available(iOS 13, *)) {
     UIWindowScene* scene = (UIWindowScene*)notification.object;
     SceneDelegate* sceneDelegate = (SceneDelegate*)scene.delegate;
@@ -251,7 +251,7 @@
 }
 
 - (void)sceneDidEnterBackground:(NSNotification*)notification {
-  DCHECK(IsMultiwindowSupported());
+  DCHECK(IsSceneStartupSupported());
   if (@available(iOS 13, *)) {
     // When the first scene enters foreground, update the app state.
     if (self.foregroundSceneCount == 0) {
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 3fb73ba..090d2b8 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -386,7 +386,7 @@
   [ProviderRegistration registerProviders];
 
   if (@available(iOS 13, *)) {
-    if (IsMultiwindowSupported()) {
+    if (IsSceneStartupSupported()) {
       // Subscribe for scene connection and disconnection notifications.
       [[NSNotificationCenter defaultCenter]
           addObserver:self
@@ -557,7 +557,7 @@
                 postCrashLaunch];
 
   if (@available(iOS 13, *)) {
-    if (IsMultiwindowSupported()) {
+    if (IsSceneStartupSupported()) {
       // The rest of the startup sequence is handled by the Scenes and in
       // response to notifications.
       return;
@@ -622,7 +622,7 @@
 
 // Handler for UISceneWillConnectNotification.
 - (void)sceneWillConnect:(NSNotification*)notification {
-  DCHECK(IsMultiwindowSupported());
+  DCHECK(IsSceneStartupSupported());
   if (@available(iOS 13, *)) {
     UIWindowScene* scene =
         base::mac::ObjCCastStrict<UIWindowScene>(notification.object);
diff --git a/ios/chrome/app/resources/BUILD.gn b/ios/chrome/app/resources/BUILD.gn
index 98b9b59..06a8372 100644
--- a/ios/chrome/app/resources/BUILD.gn
+++ b/ios/chrome/app/resources/BUILD.gn
@@ -122,12 +122,11 @@
   ]
 }
 
-if (ios_enable_multi_window) {
-  # Multi window requires both additional plist entries (handled in ../BUILD.gn) and
-  # an additional storyboard file.
-  bundle_data_ib_file("base_scene_storyboard") {
-    source = "BaseScene.storyboard"
-  }
+# The scene startup flow (which is needed for multi-window) requires both
+# additional plist entries (handled in ../BUILD.gn) and an additional storyboard
+# file.
+bundle_data_ib_file("base_scene_storyboard") {
+  source = "BaseScene.storyboard"
 }
 
 bundle_data_ib_file("launchscreen_xib") {
diff --git a/ios/chrome/app/resources/MultiWindowDisabled+Info.plist b/ios/chrome/app/resources/MultiWindowDisabled+Info.plist
new file mode 100644
index 0000000..4d25065
--- /dev/null
+++ b/ios/chrome/app/resources/MultiWindowDisabled+Info.plist
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>UIApplicationSceneManifest</key>
+	<dict>
+		<key>UIApplicationSupportsMultipleScenes</key>
+		<false/>
+		<key>UISceneConfigurations</key>
+		<dict>
+			<key>UIWindowSceneSessionRoleApplication</key>
+			<array>
+				<dict>
+					<key>UISceneClassName</key>
+					<string>UIWindowScene</string>
+					<key>UISceneDelegateClassName</key>
+					<string>SceneDelegate</string>
+					<key>UISceneConfigurationName</key>
+					<string>Default Configuration</string>
+					<key>UILaunchStoryboardName</key>
+					<string>LaunchScreen</string>
+					<key>UISceneStoryboardFile</key>
+					<string>BaseScene</string>
+				</dict>
+			</array>
+		</dict>
+	</dict>
+</dict>
+</plist>
\ No newline at end of file
diff --git a/ios/chrome/app/resources/MultiWindow+Info.plist b/ios/chrome/app/resources/MultiWindowEnabled+Info.plist
similarity index 100%
rename from ios/chrome/app/resources/MultiWindow+Info.plist
rename to ios/chrome/app/resources/MultiWindowEnabled+Info.plist
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_constants.h b/ios/chrome/browser/ui/authentication/signin/signin_constants.h
index b225ddb..74a0089 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_constants.h
+++ b/ios/chrome/browser/ui/authentication/signin/signin_constants.h
@@ -34,6 +34,14 @@
 extern NSString* const kUserSigninAttemptedNotification;
 // Name of accessibility identifier for the skip sign-in button.
 extern NSString* const kSkipSigninAccessibilityIdentifier;
+// Name of accessibility identifier for the add account button in the sign-in
+// flow.
+extern NSString* const kAddAccountAccessibilityIdentifier;
+// Name of accessibility identifier for the confirmation "Yes I'm In" sign-in
+// button.
+extern NSString* const kConfirmationAccessibilityIdentifier;
+// Name of accessibility identifier for the more button in the sign-in flow.
+extern NSString* const kMoreAccessibilityIdentifier;
 
 // Action that is required to do to complete the sign-in. This action is in
 // charge of the SigninCoordinator's owner.
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_constants.mm b/ios/chrome/browser/ui/authentication/signin/signin_constants.mm
index cabc03b..012ad0e 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_constants.mm
+++ b/ios/chrome/browser/ui/authentication/signin/signin_constants.mm
@@ -11,6 +11,11 @@
 NSString* const kUserSigninAttemptedNotification = @"kUserSigninAttempted";
 NSString* const kSkipSigninAccessibilityIdentifier =
     @"kSkipSigninAccessibilityIdentifier";
+NSString* const kAddAccountAccessibilityIdentifier =
+    @"kAddAccountAccessibilityIdentifier";
+NSString* const kConfirmationAccessibilityIdentifier =
+    @"kConfirmationAccessibilityIdentifier";
+NSString* const kMoreAccessibilityIdentifier = @"kMoreAccessibilityIdentifier";
 
 @implementation SigninCompletionInfo
 
diff --git a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm
index 03bccc965..cb57ac3 100644
--- a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm
@@ -122,17 +122,23 @@
                              forState:UIControlStateNormal];
     [self setConfirmationStylingWithButton:self.confirmationButton];
     self.confirmationButton.tag = AuthenticationButtonTypeAddAccount;
+    self.confirmationButton.accessibilityIdentifier =
+        kAddAccountAccessibilityIdentifier;
   } else if (!self.hasUnifiedConsentScreenReachedBottom) {
     // User has not scrolled to the bottom of the user consent screen.
     // Display 'more' button.
     [self updateButtonAsMoreButton:self.confirmationButton];
     self.confirmationButton.tag = AuthenticationButtonTypeMore;
+    self.confirmationButton.accessibilityIdentifier =
+        kMoreAccessibilityIdentifier;
   } else {
     // By default display 'Yes I'm in' button.
     [self.confirmationButton setTitle:self.confirmationButtonTitle
                              forState:UIControlStateNormal];
     [self setConfirmationStylingWithButton:self.confirmationButton];
     self.confirmationButton.tag = AuthenticationButtonTypeConfirmation;
+    self.confirmationButton.accessibilityIdentifier =
+        kConfirmationAccessibilityIdentifier;
   }
   [self.confirmationButton addTarget:self
                               action:@selector(onConfirmationButtonPressed:)
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn
index f6c4a42..aac15b3e 100644
--- a/ios/chrome/browser/ui/main/BUILD.gn
+++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -141,7 +141,7 @@
     "//net",
   ]
 
-  if (ios_enable_multi_window) {
+  if (ios_enable_multi_window || ios_enable_scene_startup) {
     deps += [ "//ios/chrome/app/resources:base_scene_storyboard" ]
   }
 
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index b7ce43cc..994ab30 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -206,7 +206,7 @@
     // should be created right away.
     // When multiwindow is supported, the window is created by SceneDelegate,
     // and fetched by SceneState from UIScene's windows.
-    if (!IsMultiwindowSupported() && !self.sceneState.window) {
+    if (!IsSceneStartupSupported() && !self.sceneState.window) {
       self.sceneState.window = [[ChromeOverlayWindow alloc]
           initWithFrame:[[UIScreen mainScreen] bounds]];
     }
@@ -287,9 +287,9 @@
   }
 
   DCHECK(self.mainController);
-  if (IsMultiwindowSupported()) {
+  if (IsSceneStartupSupported()) {
     // TODO(crbug.com/1012697): This should probably be the only code path for
-    // multiwindow and non-multiwindow cases.
+    // UIScene and non-UIScene cases.
     [self startUpChromeUIPostCrash:NO needRestoration:NO];
   }
 
diff --git a/ios/chrome/browser/ui/main/scene_state.mm b/ios/chrome/browser/ui/main/scene_state.mm
index 125d70dc..7867877 100644
--- a/ios/chrome/browser/ui/main/scene_state.mm
+++ b/ios/chrome/browser/ui/main/scene_state.mm
@@ -65,7 +65,7 @@
 }
 
 - (void)setWindow:(UIWindow*)window {
-  if (IsMultiwindowSupported()) {
+  if (IsSceneStartupSupported()) {
     // No need to set anything, instead the getter is backed by scene.windows
     // property.
     return;
@@ -74,7 +74,7 @@
 }
 
 - (UIWindow*)window {
-  if (IsMultiwindowSupported()) {
+  if (IsSceneStartupSupported()) {
     UIWindow* mainWindow = nil;
     if (@available(ios 13, *)) {
       for (UIWindow* window in self.scene.windows) {
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
index 82bbe06..9c04076 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
@@ -476,8 +476,7 @@
 
 // TODO(crbug.com/1046787): Test is failing for EG1.
 #if defined(CHROME_EARL_GREY_1)
-  if (![ChromeEarlGrey isIPadIdiom] &&
-      base::ios::IsRunningOnOrLater(13, 3, 0)) {
+  if (![ChromeEarlGrey isIPadIdiom]) {
     EARL_GREY_TEST_SKIPPED(@"Test skipped on Earl Grey 1.");
   }
 #endif
diff --git a/ios/chrome/browser/ui/page_info/BUILD.gn b/ios/chrome/browser/ui/page_info/BUILD.gn
index f651c11..78d434d 100644
--- a/ios/chrome/browser/ui/page_info/BUILD.gn
+++ b/ios/chrome/browser/ui/page_info/BUILD.gn
@@ -7,16 +7,8 @@
   sources = [
     "legacy_page_info_view_controller.h",
     "legacy_page_info_view_controller.mm",
-    "page_info_consumer.h",
-    "page_info_cookies_view_controller.h",
-    "page_info_cookies_view_controller.mm",
-    "page_info_description.h",
-    "page_info_description.mm",
-    "page_info_navigation_commands.h",
     "page_info_site_security_description.h",
     "page_info_site_security_description.mm",
-    "page_info_site_security_view_controller.h",
-    "page_info_site_security_view_controller.mm",
     "page_info_view_controller.h",
     "page_info_view_controller.mm",
   ]
@@ -77,8 +69,6 @@
     "page_info_coordinator.mm",
     "page_info_legacy_coordinator.h",
     "page_info_legacy_coordinator.mm",
-    "page_info_mediator.h",
-    "page_info_mediator.mm",
     "page_info_site_security_mediator.h",
     "page_info_site_security_mediator.mm",
   ]
diff --git a/ios/chrome/browser/ui/page_info/page_info_consumer.h b/ios/chrome/browser/ui/page_info/page_info_consumer.h
deleted file mode 100644
index a299867..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_consumer.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_CONSUMER_H_
-#define IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_CONSUMER_H_
-
-#import "ios/chrome/browser/ui/page_info/page_info_description.h"
-
-// A consumer for page info changes.
-@protocol PageInfoConsumer
-
-// Called when the page info has changed.
-- (void)pageInfoChanged:(PageInfoDescription*)pageInfoDescription;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/page_info/page_info_cookies_view_controller.h b/ios/chrome/browser/ui/page_info/page_info_cookies_view_controller.h
deleted file mode 100644
index c30c4a8..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_cookies_view_controller.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_COOKIES_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_COOKIES_VIEW_CONTROLLER_H_
-
-#import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
-
-// View Controller for displaying Cookies information.
-@interface PageInfoCookiesViewController : ChromeTableViewController
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_COOKIES_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/page_info/page_info_cookies_view_controller.mm b/ios/chrome/browser/ui/page_info/page_info_cookies_view_controller.mm
deleted file mode 100644
index 47612ba..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_cookies_view_controller.mm
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/page_info/page_info_cookies_view_controller.h"
-
-#import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
-#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
-#import "ios/chrome/common/ui/colors/semantic_color_names.h"
-#import "ios/chrome/common/ui/util/constraints_ui_util.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ui/base/l10n/l10n_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-typedef NS_ENUM(NSInteger, SectionIdentifier) {
-  SectionIdentifierContent = kSectionIdentifierEnumZero,
-};
-
-typedef NS_ENUM(NSInteger, ItemType) {
-  ItemTypeCookiesSwitch = kItemTypeEnumZero,
-};
-
-}  // namespace
-
-@implementation PageInfoCookiesViewController
-
-#pragma mark - UIViewController
-
-- (void)viewDidLoad {
-  [super viewDidLoad];
-
-  self.view.backgroundColor = [UIColor colorNamed:kBackgroundColor];
-  self.title = l10n_util::GetNSString(IDS_IOS_PAGE_INFO_COOKIES_TITLE);
-
-  [self loadModel];
-}
-
-#pragma mark - ChromeTableViewController
-
-- (void)loadModel {
-  [super loadModel];
-  [self.tableViewModel addSectionWithIdentifier:SectionIdentifierContent];
-
-  // Create the switch item.
-  SettingsSwitchItem* cookiesSwitchItem =
-      [[SettingsSwitchItem alloc] initWithType:ItemTypeCookiesSwitch];
-  cookiesSwitchItem.text =
-      l10n_util::GetNSString(IDS_IOS_PAGE_INFO_COOKIES_SWITCH_LABEL);
-  [self.tableViewModel addItem:cookiesSwitchItem
-       toSectionWithIdentifier:SectionIdentifierContent];
-  // TODO(crbug.com/1063824): Implement this.
-}
-
-@end
diff --git a/ios/chrome/browser/ui/page_info/page_info_coordinator.mm b/ios/chrome/browser/ui/page_info/page_info_coordinator.mm
index 0046321..5c4893a 100644
--- a/ios/chrome/browser/ui/page_info/page_info_coordinator.mm
+++ b/ios/chrome/browser/ui/page_info/page_info_coordinator.mm
@@ -6,14 +6,8 @@
 
 #include "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/reading_list/offline_page_tab_helper.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
-#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
-#import "ios/chrome/browser/ui/page_info/page_info_cookies_view_controller.h"
-#import "ios/chrome/browser/ui/page_info/page_info_mediator.h"
-#import "ios/chrome/browser/ui/page_info/page_info_navigation_commands.h"
 #import "ios/chrome/browser/ui/page_info/page_info_site_security_description.h"
 #import "ios/chrome/browser/ui/page_info/page_info_site_security_mediator.h"
-#import "ios/chrome/browser/ui/page_info/page_info_site_security_view_controller.h"
 #import "ios/chrome/browser/ui/page_info/page_info_view_controller.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/web/public/navigation/navigation_item.h"
@@ -24,11 +18,10 @@
 #error "This file requires ARC support."
 #endif
 
-@interface PageInfoCoordinator () <PageInfoNavigationCommands>
+@interface PageInfoCoordinator ()
 
 @property(nonatomic, strong) CommandDispatcher* dispatcher;
 @property(nonatomic, strong) PageInfoViewController* viewController;
-@property(nonatomic, strong) PageInfoMediator* mediator;
 @property(nonatomic, strong) UINavigationController* navigationController;
 
 @end
@@ -41,20 +34,8 @@
 #pragma mark - ChromeCoordinator
 
 - (void)start {
-  self.dispatcher = self.browser->GetCommandDispatcher();
-  [self.dispatcher
-      startDispatchingToTarget:self
-                   forProtocol:@protocol(PageInfoNavigationCommands)];
-
   self.viewController =
       [[PageInfoViewController alloc] initWithStyle:UITableViewStylePlain];
-  self.viewController.handler =
-      HandlerForProtocol(self.dispatcher, PageInfoNavigationCommands);
-
-  web::WebState* webState =
-      self.browser->GetWebStateList()->GetActiveWebState();
-  self.mediator = [[PageInfoMediator alloc] initWithWebState:webState];
-  self.mediator.consumer = self.viewController;
 
   self.navigationController = [[UINavigationController alloc]
       initWithRootViewController:self.viewController];
@@ -65,46 +46,11 @@
 }
 
 - (void)stop {
-  [self.dispatcher stopDispatchingToTarget:self];
   [self.baseViewController.presentedViewController
       dismissViewControllerAnimated:YES
                          completion:nil];
-  self.dispatcher = nil;
   self.navigationController = nil;
-  self.mediator.consumer = nil;
-  self.mediator = nil;
   self.viewController = nil;
 }
 
-#pragma mark - PageInfoNavigationCommands
-
-- (void)showSiteSecurityInfo {
-  web::WebState* webState =
-      self.browser->GetWebStateList()->GetActiveWebState();
-  web::NavigationItem* navItem =
-      webState->GetNavigationManager()->GetVisibleItem();
-
-  bool offlinePage =
-      OfflinePageTabHelper::FromWebState(webState)->presenting_offline_page();
-
-  PageInfoSiteSecurityDescription* description =
-      [PageInfoSiteSecurityMediator configurationForURL:navItem->GetURL()
-                                              SSLStatus:navItem->GetSSL()
-                                            offlinePage:offlinePage];
-
-  PageInfoSiteSecurityViewController* viewController =
-      [[PageInfoSiteSecurityViewController alloc]
-          initWitDescription:description];
-
-  viewController.handler = HandlerForProtocol(self.dispatcher, BrowserCommands);
-  [self.navigationController pushViewController:viewController animated:YES];
-}
-
-- (void)showCookiesInfo {
-  PageInfoCookiesViewController* viewController =
-      [[PageInfoCookiesViewController alloc]
-          initWithStyle:UITableViewStylePlain];
-  [self.navigationController pushViewController:viewController animated:YES];
-}
-
 @end
diff --git a/ios/chrome/browser/ui/page_info/page_info_description.h b/ios/chrome/browser/ui/page_info/page_info_description.h
deleted file mode 100644
index 0d6b1d5..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_description.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_DESCRIPTION_H_
-#define IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_DESCRIPTION_H_
-
-#import <UIKit/UIKit.h>
-
-class GURL;
-
-namespace web {
-struct SSLStatus;
-}
-
-// Create and store all the data to be displayed inside the root of the page
-// info.
-@interface PageInfoDescription : NSObject
-
-// Init based on the |URL|, the SSL |status| and if the current page is an
-// |offlinePage|.
-- (instancetype)initWithURL:(const GURL&)URL
-                  SSLStatus:(const web::SSLStatus&)status
-              isPageOffline:(BOOL)isOffline NS_DESIGNATED_INITIALIZER;
-- (instancetype)init NS_UNAVAILABLE;
-
-// Describes the security status of this page, i.e. "Page is secure" or "SSL
-// cert expired".
-@property(nonatomic, readonly) NSString* pageSecurityStatusDescription;
-
-// The icon name for the page security status, e.g. a lock.
-@property(nonatomic, readonly) NSString* pageSecurityStatusIconImageName;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_DESCRIPTION_H_
diff --git a/ios/chrome/browser/ui/page_info/page_info_description.mm b/ios/chrome/browser/ui/page_info/page_info_description.mm
deleted file mode 100644
index c288881f..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_description.mm
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/page_info/page_info_description.h"
-
-#include "base/strings/sys_string_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "components/security_state/core/security_state.h"
-#include "components/strings/grit/components_chromium_strings.h"
-#include "components/strings/grit/components_google_chrome_strings.h"
-#include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/chrome_url_constants.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ios/components/webui/web_ui_url_constants.h"
-#include "ios/web/public/security/ssl_status.h"
-#include "ui/base/l10n/l10n_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface PageInfoDescription ()
-
-@property(nonatomic, assign) web::SSLStatus status;
-@property(nonatomic, assign) const GURL& URL;
-@property(nonatomic, assign) BOOL isOffline;
-
-@end
-
-@implementation PageInfoDescription {
-  GURL _URL;
-}
-
-- (instancetype)initWithURL:(const GURL&)URL
-                  SSLStatus:(const web::SSLStatus&)status
-              isPageOffline:(BOOL)isOffline {
-  self = [super init];
-  if (self) {
-    _URL = URL;
-    _status = status;
-    _isOffline = isOffline;
-  }
-  return self;
-}
-
-- (NSString*)pageSecurityStatusDescription {
-  if (self.isOffline) {
-    return l10n_util::GetNSString(IDS_IOS_PAGE_INFO_OFFLINE_PAGE);
-  }
-
-  if (self.URL.SchemeIs(kChromeUIScheme)) {
-    return l10n_util::GetNSString(IDS_PAGE_INFO_INTERNAL_PAGE);
-  }
-
-  if (!self.status.certificate) {
-    return l10n_util::GetNSString(IDS_PAGE_INFO_NOT_SECURE_SUMMARY);
-  }
-
-  if (net::IsCertStatusError((self.status.cert_status)) ||
-      (self.status.security_style ==
-       web::SECURITY_STYLE_AUTHENTICATION_BROKEN)) {
-    // HTTPS with major errors
-    return l10n_util::GetNSString(IDS_PAGE_INFO_NOT_SECURE_SUMMARY);
-  }
-
-  if (self.status.content_status ==
-      web::SSLStatus::DISPLAYED_INSECURE_CONTENT) {
-    return l10n_util::GetNSString(IDS_PAGE_INFO_NOT_SECURE_SUMMARY);
-  }
-
-  // Valid HTTPS
-  return l10n_util::GetNSString(IDS_PAGE_INFO_SECURE_SUMMARY);
-}
-
-- (NSString*)pageSecurityStatusIconImageName {
-  // If the URL scheme corresponds to Chrome on iOS, the icon is not set.
-  if (self.URL.SchemeIs(kChromeUIScheme))
-    return @"";
-
-  if (self.isOffline)
-    return @"page_info_offline";
-
-  if (!self.status.certificate) {
-    if (security_state::ShouldShowDangerTriangleForWarningLevel())
-      return @"page_info_bad";
-
-    return @"page_info_info";
-  }
-
-  if (net::IsCertStatusError(self.status.cert_status) ||
-      self.status.security_style == web::SECURITY_STYLE_AUTHENTICATION_BROKEN)
-    // HTTPS with major errors
-    return @"page_info_bad";
-
-  // The remaining states are valid HTTPS, or HTTPS with minor errors.
-  if (self.status.content_status ==
-      web::SSLStatus::DISPLAYED_INSECURE_CONTENT) {
-    if (security_state::ShouldShowDangerTriangleForWarningLevel())
-      return @"page_info_bad";
-
-    return @"page_info";
-  }
-
-  // Valid HTTPS
-  return @"page_info_good";
-}
-
-#pragma mark - Properties
-
-- (const GURL&)URL {
-  return _URL;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/page_info/page_info_mediator.h b/ios/chrome/browser/ui/page_info/page_info_mediator.h
deleted file mode 100644
index 4d123de4..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_mediator.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_MEDIATOR_H_
-#define IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_MEDIATOR_H_
-
-#import <Foundation/Foundation.h>
-
-#include "ios/chrome/browser/main/browser.h"
-#import "ios/web/public/web_state_observer_bridge.h"
-
-@protocol PageInfoConsumer;
-
-// The mediator is pushing the data for the root of the info page page to the
-// consumer.
-@interface PageInfoMediator : NSObject
-
-- (instancetype)initWithWebState:(web::WebState*)webState
-    NS_DESIGNATED_INITIALIZER;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-// The consumer for this mediator.
-@property(nonatomic, weak) id<PageInfoConsumer> consumer;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/page_info/page_info_mediator.mm b/ios/chrome/browser/ui/page_info/page_info_mediator.mm
deleted file mode 100644
index d5fdd656..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_mediator.mm
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/page_info/page_info_mediator.h"
-
-#include "ios/chrome/browser/main/browser.h"
-#import "ios/chrome/browser/reading_list/offline_page_tab_helper.h"
-#import "ios/chrome/browser/ui/page_info/page_info_consumer.h"
-#import "ios/chrome/browser/ui/page_info/page_info_description.h"
-#import "ios/chrome/browser/web_state_list/web_state_list.h"
-#include "ios/web/public/navigation/navigation_item.h"
-#include "ios/web/public/navigation/navigation_manager.h"
-#import "ios/web/public/web_state.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface PageInfoMediator ()
-@property(nonatomic, assign) web::WebState* webState;
-@property(nonatomic, assign) web::NavigationItem* navigationItem;
-@end
-
-@implementation PageInfoMediator
-
-- (instancetype)initWithWebState:(web::WebState*)webState {
-  self = [super init];
-  if (self) {
-    _webState = webState;
-    _navigationItem = _webState->GetNavigationManager()->GetVisibleItem();
-  }
-  return self;
-}
-
-- (void)setConsumer:(id<PageInfoConsumer>)consumer {
-  if (_consumer == consumer)
-    return;
-
-  _consumer = consumer;
-  [self updateConsumer];
-}
-
-#pragma mark - Private
-
-- (void)updateConsumer {
-  const BOOL presentingOfflinePage =
-      OfflinePageTabHelper::FromWebState(_webState)->presenting_offline_page();
-
-  PageInfoDescription* description =
-      [[PageInfoDescription alloc] initWithURL:self.navigationItem->GetURL()
-                                     SSLStatus:self.navigationItem->GetSSL()
-                                 isPageOffline:presentingOfflinePage];
-  [self.consumer pageInfoChanged:description];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/page_info/page_info_navigation_commands.h b/ios/chrome/browser/ui/page_info/page_info_navigation_commands.h
deleted file mode 100644
index e5a912d..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_navigation_commands.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_NAVIGATION_COMMANDS_H_
-#define IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_NAVIGATION_COMMANDS_H_
-
-// Commands related to the Page Info navigation inside the page info view
-// controller.
-@protocol PageInfoNavigationCommands
-
-// Shows the site security information.
-- (void)showSiteSecurityInfo;
-
-// Shows the cookies information.
-- (void)showCookiesInfo;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_NAVIGATION_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/page_info/page_info_site_security_view_controller.h b/ios/chrome/browser/ui/page_info/page_info_site_security_view_controller.h
deleted file mode 100644
index 0251e9e..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_site_security_view_controller.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_SITE_SECURITY_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_SITE_SECURITY_VIEW_CONTROLLER_H_
-
-#import <UIKit/UIKit.h>
-
-#import "ios/chrome/browser/ui/page_info/page_info_site_security_description.h"
-
-@protocol BrowserCommands;
-
-// View Controller for displaying the site security.
-@interface PageInfoSiteSecurityViewController : UIViewController
-
-- (instancetype)initWitDescription:(PageInfoSiteSecurityDescription*)description
-    NS_DESIGNATED_INITIALIZER;
-
-- (instancetype)initWithNibName:(NSString*)nibNameOrNil
-                         bundle:(NSBundle*)nibBundleOrNil NS_UNAVAILABLE;
-- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
-- (instancetype)init NS_UNAVAILABLE;
-
-// Handler used to navigate outside the page info.
-@property(nonatomic, weak) id<BrowserCommands> handler;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_SITE_SECURITY_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/page_info/page_info_site_security_view_controller.mm b/ios/chrome/browser/ui/page_info/page_info_site_security_view_controller.mm
deleted file mode 100644
index ab23cd6..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_site_security_view_controller.mm
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/page_info/page_info_site_security_view_controller.h"
-
-#include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
-#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
-#import "ios/chrome/common/ui/colors/semantic_color_names.h"
-#import "ios/chrome/common/ui/util/constraints_ui_util.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ui/base/l10n/l10n_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-const CGFloat kHorizontalSpacing = 16.0f;
-
-const CGFloat kVerticalSpacing = 20.0f;
-
-const CGFloat kIconSize = 20.0f;
-
-}  // namespace
-
-@interface PageInfoSiteSecurityViewController ()
-
-@property(nonatomic, strong)
-    PageInfoSiteSecurityDescription* pageInfoSecurityDescription;
-
-@end
-
-@implementation PageInfoSiteSecurityViewController
-
-- (instancetype)initWitDescription:
-    (PageInfoSiteSecurityDescription*)description {
-  self = [super initWithNibName:nil bundle:nil];
-  if (self) {
-    _pageInfoSecurityDescription = description;
-  }
-  return self;
-}
-
-#pragma mark - UIViewController
-
-- (void)viewDidLoad {
-  [super viewDidLoad];
-
-  self.view.backgroundColor = [UIColor colorNamed:kBackgroundColor];
-  self.title = l10n_util::GetNSString(IDS_IOS_PAGE_INFO_SITE_SECURITY);
-
-  // ScrollView that contains site security information.
-  UIScrollView* scrollView = [[UIScrollView alloc] init];
-  scrollView.translatesAutoresizingMaskIntoConstraints = NO;
-  [self.view addSubview:scrollView];
-  AddSameConstraints(self.view, scrollView);
-
-  // Site security information.
-  UILabel* securitySummary = [[UILabel alloc] init];
-  securitySummary.textColor = UIColor.cr_labelColor;
-  securitySummary.numberOfLines = 0;
-  securitySummary.adjustsFontForContentSizeCategory = YES;
-  securitySummary.text = self.pageInfoSecurityDescription.message;
-  securitySummary.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
-  securitySummary.translatesAutoresizingMaskIntoConstraints = NO;
-  [scrollView addSubview:securitySummary];
-
-  // Security icon.
-  UIImage* securityIcon = [self.pageInfoSecurityDescription.image
-      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-  UIImageView* securityIconView =
-      [[UIImageView alloc] initWithImage:securityIcon];
-  securityIconView.tintColor = UIColor.cr_labelColor;
-  securityIconView.translatesAutoresizingMaskIntoConstraints = NO;
-  [scrollView addSubview:securityIconView];
-
-  // Website URL.
-  UILabel* securityURL = [[UILabel alloc] init];
-  securityURL.textColor = UIColor.cr_labelColor;
-  securityURL.adjustsFontForContentSizeCategory = YES;
-  securityURL.text = self.pageInfoSecurityDescription.title;
-  securityURL.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
-  securityURL.translatesAutoresizingMaskIntoConstraints = NO;
-  [scrollView addSubview:securityURL];
-
-  // In some cases the icon can be nil.
-  // e.g, if the URL scheme corresponds to chrome webUI, the icon is not set.
-  CGFloat iconSize = self.pageInfoSecurityDescription.image ? kIconSize : 0;
-  CGFloat iconToURLMargin =
-      self.pageInfoSecurityDescription.image ? kHorizontalSpacing : 0;
-
-  NSArray* constraints = @[
-    // SecuritySummary constraints.
-    [securitySummary.leadingAnchor
-        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor
-                       constant:kHorizontalSpacing],
-    [securitySummary.trailingAnchor
-        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor
-                       constant:-kHorizontalSpacing],
-    [securitySummary.topAnchor constraintEqualToAnchor:scrollView.topAnchor
-                                              constant:kVerticalSpacing],
-
-    // SecurityIcon constraints.
-    [securityIconView.leadingAnchor
-        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor
-                       constant:kHorizontalSpacing],
-    [securityIconView.topAnchor
-        constraintGreaterThanOrEqualToAnchor:securitySummary.bottomAnchor
-                                    constant:kVerticalSpacing],
-    [securityIconView.heightAnchor constraintEqualToConstant:iconSize],
-    [securityIconView.widthAnchor
-        constraintEqualToAnchor:securityIconView.heightAnchor],
-
-    [securityIconView.bottomAnchor
-        constraintLessThanOrEqualToAnchor:scrollView.bottomAnchor
-                                 constant:-kVerticalSpacing],
-
-    // securityURL constraints.
-    [securityURL.leadingAnchor
-        constraintEqualToAnchor:securityIconView.trailingAnchor
-                       constant:iconToURLMargin],
-    [securityURL.trailingAnchor
-        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor
-                       constant:-kHorizontalSpacing],
-    [securityURL.topAnchor
-        constraintGreaterThanOrEqualToAnchor:securitySummary.bottomAnchor
-                                    constant:kVerticalSpacing],
-    [securityURL.centerYAnchor
-        constraintEqualToAnchor:securityIconView.centerYAnchor],
-    [securityURL.bottomAnchor
-        constraintLessThanOrEqualToAnchor:scrollView.bottomAnchor
-                                 constant:-kVerticalSpacing],
-  ];
-  [NSLayoutConstraint activateConstraints:constraints];
-
-  [self addButtonActionToScrollView:scrollView
-                   securityIconView:securityIconView
-                        securityURL:securityURL];
-}
-
-#pragma mark - Private
-
-// Adds a button and his constrainsts based on the |securityIconView| and the
-// |securityURL| to the |scrollView|.
-- (void)addButtonActionToScrollView:(UIScrollView*)scrollView
-                   securityIconView:(UIImageView*)securityIconView
-                        securityURL:(UILabel*)securityURL {
-  UIButton* buttonAction =
-      [self buttonForAction:self.pageInfoSecurityDescription.buttonAction];
-  // In some cases there is no actions.
-  // e.g, if the URL scheme corresponds to chrome webUI, the button is not set.
-  if (!buttonAction)
-    return;
-
-  buttonAction.titleLabel.adjustsFontForContentSizeCategory = YES;
-  buttonAction.titleLabel.font =
-      [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
-
-  [buttonAction setTitleColor:[UIColor colorNamed:kBlueColor]
-                     forState:UIControlStateNormal];
-  buttonAction.translatesAutoresizingMaskIntoConstraints = NO;
-
-  [scrollView addSubview:buttonAction];
-
-  NSArray* buttonConstraints = @[
-    [buttonAction.leadingAnchor
-        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor
-                       constant:kHorizontalSpacing],
-    [buttonAction.leadingAnchor
-        constraintLessThanOrEqualToAnchor:self.view.safeAreaLayoutGuide
-                                              .trailingAnchor
-                                 constant:kHorizontalSpacing],
-    [buttonAction.topAnchor
-        constraintGreaterThanOrEqualToAnchor:securityIconView.bottomAnchor
-                                    constant:kVerticalSpacing],
-    [buttonAction.topAnchor
-        constraintGreaterThanOrEqualToAnchor:securityURL.bottomAnchor
-                                    constant:kVerticalSpacing],
-    [buttonAction.bottomAnchor constraintEqualToAnchor:scrollView.bottomAnchor
-                                              constant:-kVerticalSpacing],
-  ];
-  [NSLayoutConstraint activateConstraints:buttonConstraints];
-}
-
-// Returns a button with title and action configured for |buttonAction|.
-- (UIButton*)buttonForAction:(PageInfoSiteSecurityButtonAction)buttonAction {
-  UIButton* button = [UIButton buttonWithType:UIButtonTypeSystem];
-  int messageId;
-  switch (buttonAction) {
-    case PageInfoSiteSecurityButtonActionNone:
-      return nil;
-    case PageInfoSiteSecurityButtonActionShowHelp:
-      messageId = IDS_LEARN_MORE;
-      [button addTarget:self.handler
-                    action:@selector(showSecurityHelpPage)
-          forControlEvents:UIControlEventTouchUpInside];
-      break;
-    case PageInfoSiteSecurityButtonActionReload:
-      messageId = IDS_IOS_PAGE_INFO_RELOAD;
-      [button addTarget:self.handler
-                    action:@selector(hidePageInfo)
-          forControlEvents:UIControlEventTouchUpInside];
-      [button addTarget:self.handler
-                    action:@selector(reload)
-          forControlEvents:UIControlEventTouchUpInside];
-      break;
-  };
-
-  NSString* title = l10n_util::GetNSStringWithFixup(messageId);
-  [button setTitle:title forState:UIControlStateNormal];
-  return button;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/page_info/page_info_view_controller.h b/ios/chrome/browser/ui/page_info/page_info_view_controller.h
index bd9c69e6..13659191 100644
--- a/ios/chrome/browser/ui/page_info/page_info_view_controller.h
+++ b/ios/chrome/browser/ui/page_info/page_info_view_controller.h
@@ -7,16 +7,10 @@
 
 #import <UIKit/UIKit.h>
 
-#import "ios/chrome/browser/ui/page_info/page_info_consumer.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
 
-@protocol PageInfoNavigationCommands;
-
 // View Controller for displaying the page info.
-@interface PageInfoViewController : ChromeTableViewController <PageInfoConsumer>
-
-// Handler used to navigate inside the page info.
-@property(nonatomic, weak) id<PageInfoNavigationCommands> handler;
+@interface PageInfoViewController : ChromeTableViewController
 
 @end
 
diff --git a/ios/chrome/browser/ui/page_info/page_info_view_controller.mm b/ios/chrome/browser/ui/page_info/page_info_view_controller.mm
index 76d62c9..883dc808 100644
--- a/ios/chrome/browser/ui/page_info/page_info_view_controller.mm
+++ b/ios/chrome/browser/ui/page_info/page_info_view_controller.mm
@@ -6,7 +6,6 @@
 
 #include "components/content_settings/core/common/features.h"
 #import "ios/chrome/browser/ui/page_info/features.h"
-#import "ios/chrome/browser/ui/page_info/page_info_navigation_commands.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_detail_icon_item.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -15,22 +14,6 @@
 #error "This file requires ARC support."
 #endif
 
-namespace {
-
-// TODO(crbug.com/1063824): Update this.
-NSString* const kPageInfoCookiesImageName = @"";
-
-typedef NS_ENUM(NSInteger, SectionIdentifier) {
-  SectionIdentifierContent = kSectionIdentifierEnumZero,
-};
-
-typedef NS_ENUM(NSInteger, ItemType) {
-  ItemTypeSecurity = kItemTypeEnumZero,
-  ItemTypeCookies,
-};
-
-}  // namespace
-
 @implementation PageInfoViewController
 
 #pragma mark - UIViewController
@@ -41,58 +24,4 @@
   self.title = l10n_util::GetNSString(IDS_IOS_PAGE_INFO_SITE_INFORMATION);
 }
 
-#pragma mark - PageInfoConsumer
-
-- (void)pageInfoChanged:(PageInfoDescription*)pageInfoDescription {
-  [self loadModel];
-
-  [self.tableViewModel addSectionWithIdentifier:SectionIdentifierContent];
-
-  // Create the Security item.
-  TableViewDetailIconItem* securityItem =
-      [[TableViewDetailIconItem alloc] initWithType:ItemTypeSecurity];
-  securityItem.text = l10n_util::GetNSString(IDS_IOS_PAGE_INFO_SITE_SECURITY);
-  securityItem.detailText = pageInfoDescription.pageSecurityStatusDescription;
-  securityItem.iconImageName =
-      pageInfoDescription.pageSecurityStatusIconImageName;
-  securityItem.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
-  [self.tableViewModel addItem:securityItem
-       toSectionWithIdentifier:SectionIdentifierContent];
-
-  if (base::FeatureList::IsEnabled(content_settings::kImprovedCookieControls)) {
-    // Create the Cookies item.
-    TableViewDetailIconItem* CookiesItem =
-        [[TableViewDetailIconItem alloc] initWithType:ItemTypeCookies];
-    CookiesItem.text = l10n_util::GetNSString(IDS_IOS_PAGE_INFO_COOKIES_TITLE);
-    CookiesItem.detailText =
-        l10n_util::GetNSString(IDS_IOS_PAGE_INFO_COOKIES_DESCRIPTION);
-    CookiesItem.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
-    CookiesItem.iconImageName = kPageInfoCookiesImageName;
-    [self.tableViewModel addItem:CookiesItem
-         toSectionWithIdentifier:SectionIdentifierContent];
-  }
-
-  [self.tableView reloadData];
-}
-
-#pragma mark - UITableViewDelegate
-
-- (void)tableView:(UITableView*)tableView
-    didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
-  [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
-  NSInteger itemType = [self.tableViewModel itemTypeForIndexPath:indexPath];
-  switch (itemType) {
-    case ItemTypeSecurity: {
-      [self.handler showSiteSecurityInfo];
-      break;
-    }
-    case ItemTypeCookies: {
-      [self.handler showCookiesInfo];
-      break;
-    }
-    default:
-      break;
-  }
-}
-
 @end
diff --git a/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.mm b/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.mm
index 3bcdfee..fc2666a 100644
--- a/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.mm
+++ b/ios/chrome/browser/ui/qr_generator/qr_generator_view_controller.mm
@@ -42,8 +42,6 @@
   self.alwaysShowImage = YES;
   self.primaryActionBarButtonStyle = UIBarButtonSystemItemAction;
 
-  self.helpButtonAvailable = YES;
-
 #if defined(__IPHONE_13_4)
   if (@available(iOS 13.4, *)) {
     if (base::FeatureList::IsEnabled(kPointerSupport)) {
diff --git a/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm b/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm
index e92ffbc..d5086d1 100644
--- a/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm
@@ -205,12 +205,22 @@
       forSectionWithIdentifier:SectionIdentifierAccounts];
   signin::IdentityManager* identityManager =
       IdentityManagerFactory::GetForBrowserState(_browser->GetBrowserState());
+
+  NSString* authenticatedEmail = [authenticatedIdentity userEmail];
   for (const auto& account : identityManager->GetAccountsWithRefreshTokens()) {
     ChromeIdentity* identity = ios::GetChromeBrowserProvider()
                                    ->GetChromeIdentityService()
                                    ->GetIdentityWithGaiaID(account.gaia);
+    // TODO(crbug.com/1081274): This re-ordering will be redundant once we
+    // apply ordering changes to the account reconciler.
     TableViewItem* item = [self accountItem:identity];
-    [model addItem:item toSectionWithIdentifier:SectionIdentifierAccounts];
+    if ([identity.userEmail isEqual:authenticatedEmail]) {
+      [model insertItem:item
+          inSectionWithIdentifier:SectionIdentifierAccounts
+                          atIndex:0];
+    } else {
+      [model addItem:item toSectionWithIdentifier:SectionIdentifierAccounts];
+    }
 
     [mutableIdentityMap setObject:item forKey:identity.gaiaID];
   }
diff --git a/ios/chrome/browser/ui/util/BUILD.gn b/ios/chrome/browser/ui/util/BUILD.gn
index f0f7969..6bd04e6 100644
--- a/ios/chrome/browser/ui/util/BUILD.gn
+++ b/ios/chrome/browser/ui/util/BUILD.gn
@@ -8,7 +8,10 @@
 buildflag_header("ios_multi_window_buildflags") {
   header = "multi_window_buildflags.h"
   header_dir = "ios/chrome/browser/ui/util"
-  flags = [ "IOS_MULTIWINDOW_ENABLED=$ios_enable_multi_window" ]
+  flags = [
+    "IOS_MULTIWINDOW_ENABLED=$ios_enable_multi_window",
+    "IOS_SCENE_STARTUP_ENABLED=$ios_enable_scene_startup",
+  ]
 }
 
 source_set("multiwindow_util") {
diff --git a/ios/chrome/browser/ui/util/multi_window_support.h b/ios/chrome/browser/ui/util/multi_window_support.h
index 2513e4a..83c7591 100644
--- a/ios/chrome/browser/ui/util/multi_window_support.h
+++ b/ios/chrome/browser/ui/util/multi_window_support.h
@@ -11,4 +11,9 @@
 // supportsMultipleScenes] instead.
 bool IsMultiwindowSupported();
 
+// Returns true if the iOS13 UIScene-based startup flow is supported, regardless
+// of whether multiple windows are permitted. This always returns true if
+// IsMultiwindowSupported() returns true.
+bool IsSceneStartupSupported();
+
 #endif  // IOS_CHROME_BROWSER_UI_UTIL_MULTI_WINDOW_SUPPORT_H_
diff --git a/ios/chrome/browser/ui/util/multi_window_support.mm b/ios/chrome/browser/ui/util/multi_window_support.mm
index bc2bff5..50e305d 100644
--- a/ios/chrome/browser/ui/util/multi_window_support.mm
+++ b/ios/chrome/browser/ui/util/multi_window_support.mm
@@ -18,3 +18,13 @@
   return false;
 #endif
 }
+
+bool IsSceneStartupSupported() {
+  if (IsMultiwindowSupported())
+    return true;
+#if BUILDFLAG(IOS_SCENE_STARTUP_ENABLED)
+  return base::ios::IsRunningOnIOS13OrLater();
+#else
+  return false;
+#endif
+}
diff --git a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h
index 190370c..a80e549f 100644
--- a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h
+++ b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h
@@ -41,6 +41,10 @@
 // The image. Must be set before the view is loaded.
 @property(nonatomic, strong) UIImage* image;
 
+// Sets the custom spacing between the image and the title / subtitle. Must be
+// set before the view is loaded.
+@property(nonatomic, assign) CGFloat customSpacingAfterImage;
+
 // The accessibility label for the image view. If nil, the image won't be
 // accessible.
 @property(nonatomic, strong) NSString* imageAccessibilityLabel;
diff --git a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
index 0a5a97c..76d98ee 100644
--- a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
+++ b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
@@ -66,6 +66,14 @@
 
 #pragma mark - Public
 
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    _customSpacingAfterImage = kStackViewSpacingAfterIllustration;
+  }
+  return self;
+}
+
 - (void)viewDidLoad {
   [super viewDidLoad];
 
@@ -78,8 +86,8 @@
   UILabel* title = [self createTitleLabel];
   UILabel* subtitle = [self createSubtitleLabel];
 
-  self.stackView = [self
-      createStackViewWithArrangedSubviews:@[ self.imageView, title, subtitle ]];
+  NSArray* stackSubviews = @[ self.imageView, title, subtitle ];
+  self.stackView = [self createStackViewWithArrangedSubviews:stackSubviews];
 
   UIScrollView* scrollView = [self createScrollView];
   [scrollView addSubview:self.stackView];
@@ -447,7 +455,7 @@
     (NSArray<UIView*>*)subviews {
   UIStackView* stackView =
       [[UIStackView alloc] initWithArrangedSubviews:subviews];
-  [stackView setCustomSpacing:kStackViewSpacingAfterIllustration
+  [stackView setCustomSpacing:self.customSpacingAfterImage
                     afterView:self.imageView];
 
   if (self.imageHasFixedSize) {
diff --git a/ios/chrome/credential_provider_extension/ui/consent_view_controller.mm b/ios/chrome/credential_provider_extension/ui/consent_view_controller.mm
index 1b135ed5..baad69e 100644
--- a/ios/chrome/credential_provider_extension/ui/consent_view_controller.mm
+++ b/ios/chrome/credential_provider_extension/ui/consent_view_controller.mm
@@ -8,12 +8,18 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+constexpr CGFloat kStackViewSpacingAfterIllustration = 37;
+}  // namespace
+
 @implementation ConsentViewController
 
 #pragma mark - Public
 
 - (void)loadView {
   self.image = [UIImage imageNamed:@"consent_illustration"];
+  self.customSpacingAfterImage = kStackViewSpacingAfterIllustration;
+
   self.helpButtonAvailable = YES;
   self.primaryActionAvailable = YES;
   NSString* titleString =
diff --git a/ios/chrome/credential_provider_extension/ui/empty_credentials_view_controller.mm b/ios/chrome/credential_provider_extension/ui/empty_credentials_view_controller.mm
index 2df7db2a..ef4c812f 100644
--- a/ios/chrome/credential_provider_extension/ui/empty_credentials_view_controller.mm
+++ b/ios/chrome/credential_provider_extension/ui/empty_credentials_view_controller.mm
@@ -8,12 +8,18 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+constexpr CGFloat kStackViewSpacingAfterIllustration = 32;
+}  // namespace
+
 @implementation EmptyCredentialsViewController
 
 #pragma mark - Public
 
 - (void)loadView {
   self.image = [UIImage imageNamed:@"empty_credentials_illustration"];
+  self.customSpacingAfterImage = kStackViewSpacingAfterIllustration;
+
   self.helpButtonAvailable = NO;
   self.primaryActionAvailable = NO;
   NSString* titleString =
diff --git a/ios/chrome/credential_provider_extension/ui/resources/BUILD.gn b/ios/chrome/credential_provider_extension/ui/resources/BUILD.gn
index de96e6e..cc216de 100644
--- a/ios/chrome/credential_provider_extension/ui/resources/BUILD.gn
+++ b/ios/chrome/credential_provider_extension/ui/resources/BUILD.gn
@@ -13,31 +13,26 @@
     ":info_icon",
     ":password_hide_icon",
     ":password_reveal_icon",
-    ":stale_credentials_illustration",
   ]
 }
 
 imageset("consent_illustration") {
   sources = [
     "consent_illustration.imageset/Contents.json",
-    "consent_illustration.imageset/illustration_dark.png",
-    "consent_illustration.imageset/illustration_light.png",
+    "consent_illustration.imageset/illustration_dark@2x.png",
+    "consent_illustration.imageset/illustration_dark@3x.png",
+    "consent_illustration.imageset/illustration_light@2x.png",
+    "consent_illustration.imageset/illustration_light@3x.png",
   ]
 }
 
 imageset("empty_credentials_illustration") {
   sources = [
     "empty_credentials_illustration.imageset/Contents.json",
-    "empty_credentials_illustration.imageset/illustration_dark.png",
-    "empty_credentials_illustration.imageset/illustration_light.png",
-  ]
-}
-
-imageset("stale_credentials_illustration") {
-  sources = [
-    "stale_credentials_illustration.imageset/Contents.json",
-    "stale_credentials_illustration.imageset/illustration_dark.png",
-    "stale_credentials_illustration.imageset/illustration_light.png",
+    "empty_credentials_illustration.imageset/illustration_dark@2x.png",
+    "empty_credentials_illustration.imageset/illustration_dark@3x.png",
+    "empty_credentials_illustration.imageset/illustration_light@2x.png",
+    "empty_credentials_illustration.imageset/illustration_light@3x.png",
   ]
 }
 
diff --git a/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/Contents.json b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/Contents.json
index e5b1b4d6..1cdd7c5 100644
--- a/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/Contents.json
+++ b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/Contents.json
@@ -2,21 +2,53 @@
   "images" : [
     {
       "idiom" : "universal",
-      "filename" : "illustration_light.png"
+      "scale" : "1x"
     },
     {
-      "idiom" : "universal",
-      "filename" : "illustration_dark.png",
       "appearances" : [
         {
           "appearance" : "luminosity",
           "value" : "dark"
         }
-      ]
+      ],
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "illustration_light@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "filename" : "illustration_dark@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "illustration_light@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "filename" : "illustration_dark@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
     }
   ],
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
   }
-}
\ No newline at end of file
+}
diff --git a/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_dark.png b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_dark.png
deleted file mode 100644
index dc7bb7b..0000000
--- a/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_dark.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_dark@2x.png b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_dark@2x.png
new file mode 100644
index 0000000..77ee54c
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_dark@2x.png
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_dark@3x.png b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_dark@3x.png
new file mode 100644
index 0000000..52515ec9c
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_dark@3x.png
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_light.png b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_light.png
deleted file mode 100644
index 9edcfcba..0000000
--- a/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_light.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_light@2x.png b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_light@2x.png
new file mode 100644
index 0000000..d71cfd0
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_light@2x.png
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_light@3x.png b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_light@3x.png
new file mode 100644
index 0000000..2b6deb01
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/resources/consent_illustration.imageset/illustration_light@3x.png
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/Contents.json b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/Contents.json
index e5b1b4d6..1cdd7c5 100644
--- a/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/Contents.json
+++ b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/Contents.json
@@ -2,21 +2,53 @@
   "images" : [
     {
       "idiom" : "universal",
-      "filename" : "illustration_light.png"
+      "scale" : "1x"
     },
     {
-      "idiom" : "universal",
-      "filename" : "illustration_dark.png",
       "appearances" : [
         {
           "appearance" : "luminosity",
           "value" : "dark"
         }
-      ]
+      ],
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "illustration_light@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "filename" : "illustration_dark@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "illustration_light@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "filename" : "illustration_dark@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
     }
   ],
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
   }
-}
\ No newline at end of file
+}
diff --git a/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_dark.png b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_dark.png
deleted file mode 100644
index dc7bb7b..0000000
--- a/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_dark.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_dark@2x.png b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_dark@2x.png
new file mode 100644
index 0000000..7231a38
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_dark@2x.png
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_dark@3x.png b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_dark@3x.png
new file mode 100644
index 0000000..d9a4709
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_dark@3x.png
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_light.png b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_light.png
deleted file mode 100644
index 9edcfcba..0000000
--- a/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_light.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_light@2x.png b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_light@2x.png
new file mode 100644
index 0000000..e3a4bbd88
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_light@2x.png
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_light@3x.png b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_light@3x.png
new file mode 100644
index 0000000..663e03f
--- /dev/null
+++ b/ios/chrome/credential_provider_extension/ui/resources/empty_credentials_illustration.imageset/illustration_light@3x.png
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/stale_credentials_illustration.imageset/Contents.json b/ios/chrome/credential_provider_extension/ui/resources/stale_credentials_illustration.imageset/Contents.json
deleted file mode 100644
index e5b1b4d6..0000000
--- a/ios/chrome/credential_provider_extension/ui/resources/stale_credentials_illustration.imageset/Contents.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "illustration_light.png"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "illustration_dark.png",
-      "appearances" : [
-        {
-          "appearance" : "luminosity",
-          "value" : "dark"
-        }
-      ]
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/ios/chrome/credential_provider_extension/ui/resources/stale_credentials_illustration.imageset/illustration_dark.png b/ios/chrome/credential_provider_extension/ui/resources/stale_credentials_illustration.imageset/illustration_dark.png
deleted file mode 100644
index dc7bb7b..0000000
--- a/ios/chrome/credential_provider_extension/ui/resources/stale_credentials_illustration.imageset/illustration_dark.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/resources/stale_credentials_illustration.imageset/illustration_light.png b/ios/chrome/credential_provider_extension/ui/resources/stale_credentials_illustration.imageset/illustration_light.png
deleted file mode 100644
index 9edcfcba..0000000
--- a/ios/chrome/credential_provider_extension/ui/resources/stale_credentials_illustration.imageset/illustration_light.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/credential_provider_extension/ui/stale_credentials_view_controller.mm b/ios/chrome/credential_provider_extension/ui/stale_credentials_view_controller.mm
index 7a0bd33..6a05a7f 100644
--- a/ios/chrome/credential_provider_extension/ui/stale_credentials_view_controller.mm
+++ b/ios/chrome/credential_provider_extension/ui/stale_credentials_view_controller.mm
@@ -8,12 +8,18 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+constexpr CGFloat kStackViewSpacingAfterIllustration = 32;
+}  // namespace
+
 @implementation StaleCredentialsViewController
 
 #pragma mark - Public
 
 - (void)loadView {
-  self.image = [UIImage imageNamed:@"stale_credentials_illustration"];
+  self.image = [UIImage imageNamed:@"empty_credentials_illustration"];
+  self.customSpacingAfterImage = kStackViewSpacingAfterIllustration;
+
   self.helpButtonAvailable = NO;
   self.primaryActionAvailable = NO;
   NSString* titleString =
diff --git a/ios/third_party/material_components_ios/BUILD.gn b/ios/third_party/material_components_ios/BUILD.gn
index f0fb742..53d5e054 100644
--- a/ios/third_party/material_components_ios/BUILD.gn
+++ b/ios/third_party/material_components_ios/BUILD.gn
@@ -288,6 +288,7 @@
   "src/components/ProgressView/src/MaterialProgressView.h",
   "src/components/ProgressView/src/Theming/MDCProgressView+MaterialTheming.h",
   "src/components/ProgressView/src/Theming/MaterialProgressView+Theming.h",
+  "src/components/ProgressView/src/private/MDCProgressGradientView.h",
   "src/components/ProgressView/src/private/MaterialProgressViewStrings.h",
   "src/components/ProgressView/src/private/MaterialProgressViewStrings_table.h",
   "src/components/Ripple/src/MDCRippleTouchController.h",
@@ -342,21 +343,21 @@
   "src/components/Snackbar/src/private/MDCSnackbarOverlayView.h",
   "src/components/Snackbar/src/private/MaterialSnackbarStrings.h",
   "src/components/Snackbar/src/private/MaterialSnackbarStrings_table.h",
+  "src/components/Tabs/src/ExtendedAlignment/MDCTabBarExtendedAlignment.h",
   "src/components/Tabs/src/MDCTabBar.h",
   "src/components/Tabs/src/MDCTabBarAlignment.h",
   "src/components/Tabs/src/MDCTabBarControllerDelegate.h",
   "src/components/Tabs/src/MDCTabBarDelegate.h",
   "src/components/Tabs/src/MDCTabBarDisplayDelegate.h",
-  "src/components/Tabs/src/MDCTabBarExtendedAlignment.h",
   "src/components/Tabs/src/MDCTabBarIndicatorAttributes.h",
   "src/components/Tabs/src/MDCTabBarIndicatorContext.h",
   "src/components/Tabs/src/MDCTabBarIndicatorTemplate.h",
   "src/components/Tabs/src/MDCTabBarItemAppearance.h",
-  "src/components/Tabs/src/MDCTabBarSizeClassDelegate.h",
   "src/components/Tabs/src/MDCTabBarTextTransform.h",
   "src/components/Tabs/src/MDCTabBarUnderlineIndicatorTemplate.h",
   "src/components/Tabs/src/MDCTabBarViewController.h",
   "src/components/Tabs/src/MaterialTabs.h",
+  "src/components/Tabs/src/SizeClassDelegate/MDCTabBarSizeClassDelegate.h",
   "src/components/Tabs/src/TabBarView/MDCTabBarItem.h",
   "src/components/Tabs/src/TabBarView/MDCTabBarItemCustomViewing.h",
   "src/components/Tabs/src/TabBarView/MDCTabBarView.h",
@@ -636,6 +637,8 @@
   "src/components/Snackbar/src/TypographyThemer",
   "src/components/Snackbar/src/private",
   "src/components/Tabs/src",
+  "src/components/Tabs/src/ExtendedAlignment",
+  "src/components/Tabs/src/SizeClassDelegate",
   "src/components/Tabs/src/TabBarView",
   "src/components/Tabs/src/TabBarView/private",
   "src/components/Tabs/src/Theming",
@@ -1112,6 +1115,8 @@
   "src/components/ProgressView/src/Theming/MDCProgressView+MaterialTheming.h",
   "src/components/ProgressView/src/Theming/MDCProgressView+MaterialTheming.m",
   "src/components/ProgressView/src/Theming/MaterialProgressView+Theming.h",
+  "src/components/ProgressView/src/private/MDCProgressGradientView.h",
+  "src/components/ProgressView/src/private/MDCProgressGradientView.m",
   "src/components/ProgressView/src/private/MaterialProgressViewStrings.h",
   "src/components/ProgressView/src/private/MaterialProgressViewStrings_table.h",
   "src/components/Ripple/src/MDCRippleTouchController.h",
@@ -1194,25 +1199,25 @@
   "src/components/Snackbar/src/private/MDCSnackbarOverlayView.m",
   "src/components/Snackbar/src/private/MaterialSnackbarStrings.h",
   "src/components/Snackbar/src/private/MaterialSnackbarStrings_table.h",
+  "src/components/Tabs/src/ExtendedAlignment/MDCTabBarExtendedAlignment.h",
   "src/components/Tabs/src/MDCTabBar.h",
   "src/components/Tabs/src/MDCTabBar.m",
   "src/components/Tabs/src/MDCTabBarAlignment.h",
   "src/components/Tabs/src/MDCTabBarControllerDelegate.h",
   "src/components/Tabs/src/MDCTabBarDelegate.h",
   "src/components/Tabs/src/MDCTabBarDisplayDelegate.h",
-  "src/components/Tabs/src/MDCTabBarExtendedAlignment.h",
   "src/components/Tabs/src/MDCTabBarIndicatorAttributes.h",
   "src/components/Tabs/src/MDCTabBarIndicatorAttributes.m",
   "src/components/Tabs/src/MDCTabBarIndicatorContext.h",
   "src/components/Tabs/src/MDCTabBarIndicatorTemplate.h",
   "src/components/Tabs/src/MDCTabBarItemAppearance.h",
-  "src/components/Tabs/src/MDCTabBarSizeClassDelegate.h",
   "src/components/Tabs/src/MDCTabBarTextTransform.h",
   "src/components/Tabs/src/MDCTabBarUnderlineIndicatorTemplate.h",
   "src/components/Tabs/src/MDCTabBarUnderlineIndicatorTemplate.m",
   "src/components/Tabs/src/MDCTabBarViewController.h",
   "src/components/Tabs/src/MDCTabBarViewController.m",
   "src/components/Tabs/src/MaterialTabs.h",
+  "src/components/Tabs/src/SizeClassDelegate/MDCTabBarSizeClassDelegate.h",
   "src/components/Tabs/src/TabBarView/MDCTabBarItem.h",
   "src/components/Tabs/src/TabBarView/MDCTabBarItem.m",
   "src/components/Tabs/src/TabBarView/MDCTabBarItemCustomViewing.h",
diff --git a/media/gpu/vaapi/vaapi_image_processor_backend.cc b/media/gpu/vaapi/vaapi_image_processor_backend.cc
index 6f63daf..4cb6bce 100644
--- a/media/gpu/vaapi/vaapi_image_processor_backend.cc
+++ b/media/gpu/vaapi/vaapi_image_processor_backend.cc
@@ -25,6 +25,7 @@
 
 namespace media {
 
+#if defined(OS_CHROMEOS)
 namespace {
 // UMA errors that the VaapiImageProcessorBackend class reports.
 enum class VaIPFailure {
@@ -67,6 +68,7 @@
 }
 
 }  // namespace
+#endif
 
 // static
 std::unique_ptr<ImageProcessorBackend> VaapiImageProcessorBackend::Create(
@@ -78,8 +80,7 @@
 // VaapiImageProcessorBackend supports ChromeOS only.
 #if !defined(OS_CHROMEOS)
   return nullptr;
-#endif
-
+#else
   auto input_vafourcc = input_config.fourcc.ToVAFourCC();
   if (!input_vafourcc) {
     VLOGF(2) << "Input fourcc " << input_config.fourcc.ToString()
@@ -146,6 +147,7 @@
   return base::WrapUnique<ImageProcessorBackend>(new VaapiImageProcessorBackend(
       std::move(vaapi_wrapper), input_config, output_config, OutputMode::IMPORT,
       std::move(error_cb), std::move(backend_task_runner)));
+#endif
 }
 
 VaapiImageProcessorBackend::VaapiImageProcessorBackend(
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index e5aed6e..07e0901 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -101,6 +101,11 @@
 #endif
 };
 
+// Enables Cross-Origin Opener Policy (COOP) reporting.
+// https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
+const base::Feature kCrossOriginOpenerPolicyReporting{
+    "CrossOriginOpenerPolicyReporting", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables Cross-Origin Embedder Policy (COEP).
 // https://github.com/mikewest/corpp
 // Currently this feature is enabled for all platforms except WebView.
diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h
index e85a3c1d..7bc964d 100644
--- a/services/network/public/cpp/features.h
+++ b/services/network/public/cpp/features.h
@@ -39,6 +39,8 @@
 COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kCrossOriginOpenerPolicy;
 COMPONENT_EXPORT(NETWORK_CPP)
+extern const base::Feature kCrossOriginOpenerPolicyReporting;
+COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kCrossOriginEmbedderPolicy;
 COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kBlockNonSecureExternalRequests;
diff --git a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
index 67b1bdd5..5adac0c 100644
--- a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
+++ b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc
@@ -44,7 +44,6 @@
 #endif
 
 #if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
-#include "base/threading/platform_thread.h"
 #include "services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.h"
 #endif
 
@@ -193,54 +192,8 @@
 }
 
 #if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
-// The loader lock is process-wide so should only be sampled on a single
-// thread.
-base::PlatformThreadId g_loader_lock_sample_thread_id = base::kInvalidThreadId;
-
-bool g_loader_lock_is_held = false;
-
 TracingSamplerProfiler::LoaderLockSampler* g_test_loader_lock_sampler = nullptr;
-
-void InitializeLoaderLockSamplingForThread(base::PlatformThreadId thread_id) {
-  InitializeLoaderLockSampling();
-
-  g_loader_lock_sample_thread_id = thread_id;
-
-  // InitializeLoaderLockSamplingForThread can be called multiple times during
-  // tests. Make sure each test starts from a known state.
-  g_loader_lock_is_held = false;
-}
-
-// Checks the state of the loader lock while samples are being taken for thread
-// |thread_id|. This function should only be called from the profiler thread
-// since it updates global data.
-void SampleLoaderLockForThread(base::PlatformThreadId thread_id) {
-  if (thread_id != g_loader_lock_sample_thread_id)
-    return;
-
-  bool loader_lock_now_held =
-      g_test_loader_lock_sampler
-          ? g_test_loader_lock_sampler->IsLoaderLockHeld()
-          : IsLoaderLockHeld();
-
-  // TODO(crbug.com/1065077): It would be cleaner to save the loader lock state
-  // alongside buffered_samples_ and then add it to the ProcessDescriptor
-  // packet in
-  // TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace. But
-  // ProcessDescriptor is currently not being collected correctly. See the full
-  // discussion in the linked crbug.
-  if (loader_lock_now_held && !g_loader_lock_is_held) {
-    TRACE_EVENT_ASYNC_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
-                             TracingSamplerProfiler::kLoaderLockHeldEventName,
-                             thread_id);
-  } else if (!loader_lock_now_held && g_loader_lock_is_held) {
-    TRACE_EVENT_ASYNC_END0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
-                           TracingSamplerProfiler::kLoaderLockHeldEventName,
-                           thread_id);
-  }
-  g_loader_lock_is_held = loader_lock_now_held;
-}
-#endif  // BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
+#endif
 
 }  // namespace
 
@@ -296,7 +249,7 @@
     std::vector<base::Frame> frames,
     base::TimeTicks sample_timestamp) {
 #if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
-  SampleLoaderLockForThread(sampled_thread_id_);
+  SampleLoaderLock();
 #endif
 
   base::AutoLock l(trace_writer_lock_);
@@ -537,6 +490,35 @@
   return interned_callstack.id;
 }
 
+#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
+void TracingSamplerProfiler::TracingProfileBuilder::SampleLoaderLock() {
+  if (!should_sample_loader_lock_)
+    return;
+
+  bool loader_lock_now_held =
+      g_test_loader_lock_sampler
+          ? g_test_loader_lock_sampler->IsLoaderLockHeld()
+          : IsLoaderLockHeld();
+
+  // TODO(crbug.com/1065077): It would be cleaner to save the loader lock state
+  // alongside buffered_samples_ and then add it to the ProcessDescriptor
+  // packet in
+  // TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace. But
+  // ProcessDescriptor is currently not being collected correctly. See the full
+  // discussion in the linked crbug.
+  if (loader_lock_now_held && !loader_lock_is_held_) {
+    TRACE_EVENT_ASYNC_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
+                             TracingSamplerProfiler::kLoaderLockHeldEventName,
+                             this);
+  } else if (!loader_lock_now_held && loader_lock_is_held_) {
+    TRACE_EVENT_ASYNC_END0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
+                           TracingSamplerProfiler::kLoaderLockHeldEventName,
+                           this);
+  }
+  loader_lock_is_held_ = loader_lock_now_held;
+}
+#endif
+
 // static
 void TracingSamplerProfiler::MangleModuleIDIfNeeded(std::string* module_id) {
 #if defined(OS_ANDROID) || defined(OS_LINUX)
@@ -559,11 +541,15 @@
 // static
 std::unique_ptr<TracingSamplerProfiler>
 TracingSamplerProfiler::CreateOnMainThread() {
-  auto thread_token = base::GetSamplingProfilerCurrentThreadToken();
+  auto profiler = std::make_unique<TracingSamplerProfiler>(
+      base::GetSamplingProfilerCurrentThreadToken());
 #if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
-  InitializeLoaderLockSamplingForThread(thread_token.id);
+  // The loader lock is process-wide so should only be sampled on a single
+  // thread. The main thread is convenient.
+  InitializeLoaderLockSampling();
+  profiler->EnableLoaderLockSampling();
 #endif
-  return std::make_unique<TracingSamplerProfiler>(std::move(thread_token));
+  return profiler;
 }
 
 // static
@@ -662,6 +648,11 @@
   auto profile_builder = std::make_unique<TracingProfileBuilder>(
       sampled_thread_token_.id, std::move(trace_writer),
       should_enable_filtering, sample_callback_for_testing_);
+#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
+  if (should_sample_loader_lock_)
+    profile_builder->EnableLoaderLockSampling();
+#endif
+
   profile_builder_ = profile_builder.get();
   // Create and start the stack sampling profiler.
 #if defined(OS_ANDROID)
diff --git a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
index 9f01baf..c786476 100644
--- a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
+++ b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h
@@ -52,6 +52,12 @@
             base::RepeatingClosure());
     ~TracingProfileBuilder() override;
 
+#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
+    void EnableLoaderLockSampling() { should_sample_loader_lock_ = true; }
+
+    void SampleLoaderLock();
+#endif
+
     // base::ProfileBuilder
     base::ModuleCache* GetModuleCache() override;
     void OnSampleCompleted(std::vector<base::Frame> frames,
@@ -104,6 +110,11 @@
     base::TimeTicks last_timestamp_;
     const bool should_enable_filtering_;
     base::RepeatingClosure sample_callback_for_testing_;
+
+#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
+    bool should_sample_loader_lock_ = false;
+    bool loader_lock_is_held_ = false;
+#endif
   };
 
 #if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
@@ -119,6 +130,12 @@
   // The name of a trace event that will be recorded when the loader lock is
   // held.
   static const char kLoaderLockHeldEventName[];
+
+  // Registers a mock LoaderLockSampler to be called during tests. |sampler| is
+  // owned by the caller. It must be reset to |nullptr| at the end of the test.
+  static void SetLoaderLockSamplerForTesting(LoaderLockSampler* sampler);
+
+  void EnableLoaderLockSampling() { should_sample_loader_lock_ = true; }
 #endif
 
   // Creates sampling profiler on main thread. The profiler *must* be
@@ -140,12 +157,6 @@
   static void StopTracingForTesting();
   static void MangleModuleIDIfNeeded(std::string* module_id);
 
-#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
-  // Registers a mock LoaderLockSampler to be called during tests. |sampler| is
-  // owned by the caller. It must be reset to |nullptr| at the end of the test.
-  static void SetLoaderLockSamplerForTesting(LoaderLockSampler* sampler);
-#endif
-
   // Returns whether of not the sampler profiling is able to unwind the stack
   // on this platform.
   constexpr static bool IsStackUnwindingSupported() {
@@ -179,6 +190,10 @@
   TracingProfileBuilder* profile_builder_ = nullptr;
   base::RepeatingClosure sample_callback_for_testing_;
 
+#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
+  bool should_sample_loader_lock_ = false;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(TracingSamplerProfiler);
 };
 
diff --git a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc
index 13aba1c..0be5803 100644
--- a/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc
+++ b/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc
@@ -483,15 +483,25 @@
   EXPECT_EQ(event_analyzer.CountEvents(), 0U);
 }
 
+TEST_F(TracingSampleProfilerTest, SampleLoaderLockWithoutMock) {
+  // Use the real loader lock sampler. This tests that it is initialized
+  // correctly in TracingSamplerProfiler.
+  TracingSamplerProfiler::SetLoaderLockSamplerForTesting(nullptr);
+
+  auto profiler = TracingSamplerProfiler::CreateOnMainThread();
+  BeginTrace();
+  base::RunLoop().RunUntilIdle();
+  WaitForEvents();
+  EndTracing();
+  base::RunLoop().RunUntilIdle();
+
+  // The loader lock may or may not be held during the test, so there's no
+  // output to test. The test passes if it reaches the end without crashing.
+}
+
 #endif  // BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
 
-// Crashes on Windows.  https://crbug.com/1076768
-#if defined(OS_WIN)
-#define MAYBE_ValidModule DISABLED_ValidModule
-#else
-#define MAYBE_ValidModule ValidModule
-#endif
-TEST(TracingProfileBuilderTest, MAYBE_ValidModule) {
+TEST(TracingProfileBuilderTest, ValidModule) {
   TestModule module;
   TracingSamplerProfiler::TracingProfileBuilder profile_builder(
       base::PlatformThreadId(),
@@ -500,13 +510,7 @@
                                     base::TimeTicks());
 }
 
-// Crashes on Win7 only.  http://crbug.com/1076768
-#if defined(OS_WIN)
-#define MAYBE_InvalidModule DISABLED_InvalidModule
-#else
-#define MAYBE_InvalidModule InvalidModule
-#endif
-TEST(TracingProfileBuilderTest, MAYBE_InvalidModule) {
+TEST(TracingProfileBuilderTest, InvalidModule) {
   TracingSamplerProfiler::TracingProfileBuilder profile_builder(
       base::PlatformThreadId(),
       std::make_unique<MockTraceWriter>(base::DoNothing()), false);
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 3fdbe99..c1c2aef 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -109,6 +109,10 @@
         ],
         "isolate_name": "performance_test_suite",
         "merge": {
+          "args": [
+            "--lightweight",
+            "--skip-perf"
+          ],
           "script": "//tools/perf/process_perf_results.py"
         },
         "name": "performance_test_suite",
@@ -144,6 +148,14 @@
       }
     ]
   },
+  "android-pixel2-processor-perf-fyi": {
+    "merge": {
+      "args": [
+        "--lightweight"
+      ],
+      "script": "//tools/perf/process_perf_results.py"
+    }
+  },
   "chromeos-kevin-perf-fyi": {
     "isolated_scripts": [
       {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 47a4a125..6315163 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -538,6 +538,32 @@
             ]
         }
     ],
+    "AutofillAPIServer": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "android_webview",
+                "chromeos",
+                "ios",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "API_Enabled",
+                    "params": {
+                        "autofill-server-url": "https://content-autofill.googleapis.com/"
+                    },
+                    "enable_features": [
+                        "AutofillServerCommunication",
+                        "AutofillUseApi"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillAssistantChromeEntryLaunch": [
         {
             "platforms": [
@@ -750,32 +776,6 @@
             ]
         }
     ],
-    "AutofillServerBehaviors": [
-        {
-            "platforms": [
-                "android",
-                "android_weblayer",
-                "android_webview",
-                "chromeos",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "API_Enabled",
-                    "params": {
-                        "autofill-server-url": "https://content-autofill.googleapis.com/"
-                    },
-                    "enable_features": [
-                        "AutofillServerCommunication",
-                        "AutofillUseApi"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutofillSmallFormSupport": [
         {
             "platforms": [
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 3f53f797e..59eb599 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -377,6 +377,7 @@
     "web/web_plugin_script_forbidden_scope.h",
     "web/web_popup_menu_info.h",
     "web/web_prerenderer_client.h",
+    "web/web_print_page_description.h",
     "web/web_print_params.h",
     "web/web_print_preset_options.h",
     "web/web_print_scaling_option.h",
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index d0795849..3a1668bd 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -54,7 +54,6 @@
 class WebContentCaptureClient;
 class WebContentSettingsClient;
 class WebDocument;
-class WebDoubleSize;
 class WebDOMMessageEvent;
 class WebLocalFrameClient;
 class WebFrameWidget;
@@ -73,6 +72,7 @@
 struct WebAssociatedURLLoaderOptions;
 struct WebConsoleMessage;
 struct WebIsolatedWorldInfo;
+struct WebPrintPageDescription;
 struct WebPrintParams;
 struct WebPrintPresetOptions;
 struct WebScriptSource;
@@ -278,16 +278,11 @@
   // Returns the type of @page size styling for the given page.
   virtual PageSizeType GetPageSizeType(int page_index) = 0;
 
-  // Returns the preferred page size and margins in pixels, assuming 96
-  // pixels per inch. pageSize, marginTop, marginRight, marginBottom,
-  // marginLeft must be initialized to the default values that are used if
-  // auto is specified.
-  virtual void PageSizeAndMarginsInPixels(int page_index,
-                                          WebDoubleSize& page_size,
-                                          int& margin_top,
-                                          int& margin_right,
-                                          int& margin_bottom,
-                                          int& margin_left) = 0;
+  // Gets the description for the specified page. This includes preferred page
+  // size and margins in pixels, assuming 96 pixels per inch. The size and
+  // margins must be initialized to the default values that are used if auto is
+  // specified.
+  virtual void GetPageDescription(int page_index, WebPrintPageDescription*) = 0;
 
   // Scripting --------------------------------------------------------------
 
diff --git a/third_party/blink/public/web/web_print_page_description.h b/third_party/blink/public/web/web_print_page_description.h
new file mode 100644
index 0000000..e7447bd
--- /dev/null
+++ b/third_party/blink/public/web/web_print_page_description.h
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_PRINT_PAGE_DESCRIPTION_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_PRINT_PAGE_DESCRIPTION_H_
+
+#include "third_party/blink/public/platform/web_double_size.h"
+
+namespace blink {
+
+// Description of a specific page when printing. All sizes are in pixels.
+struct WebPrintPageDescription {
+  WebDoubleSize size;
+  int margin_top = 0;
+  int margin_right = 0;
+  int margin_bottom = 0;
+  int margin_left = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_PRINT_PAGE_DESCRIPTION_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
index 91fd8252..a6418f7 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
@@ -555,9 +555,7 @@
                                         "because it was not transferred.");
       return false;
     }
-    if (stream->IsLocked(script_state_, exception_state).value_or(true)) {
-      if (exception_state.HadException())
-        return false;
+    if (stream->locked()) {
       exception_state.ThrowDOMException(
           DOMExceptionCode::kDataCloneError,
           "A WritableStream could not be cloned because it was locked");
@@ -587,9 +585,7 @@
     if (stream->Readable()
             ->IsLocked(script_state_, exception_state)
             .value_or(true) ||
-        stream->Writable()
-            ->IsLocked(script_state_, exception_state)
-            .value_or(true)) {
+        stream->Writable()->locked()) {
       if (exception_state.HadException())
         return false;
       exception_state.ThrowDOMException(
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc b/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
index 914f3f7..e4c80762 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
@@ -182,24 +182,19 @@
   void VisitRoot(const void*, TraceDescriptor, const base::Location&) final;
   void Visit(const TraceWrapperV8Reference<v8::Value>&) final;
   void Visit(const void*, TraceDescriptor) final;
-  void VisitBackingStoreStrongly(const void*,
-                                 const void* const*,
-                                 TraceDescriptor) final;
-  void VisitBackingStoreWeakly(const void*,
-                               const void* const*,
-                               TraceDescriptor,
-                               TraceDescriptor,
-                               WeakCallback,
-                               const void*) final;
   void VisitEphemeron(const void*, const void*, TraceCallback) final;
+  void VisitWeakContainer(const void*,
+                          const void* const*,
+                          TraceDescriptor,
+                          TraceDescriptor,
+                          WeakCallback,
+                          const void*) final;
 
   // Unused Visitor overrides.
   void VisitWeak(const void* object,
                  const void* object_weak_ref,
                  TraceDescriptor desc,
                  WeakCallback callback) final {}
-  void VisitBackingStoreOnly(const void*, const void* const*) final {}
-  void RegisterBackingStoreCallback(const void*, MovingObjectCallback) final {}
   void RegisterWeakCallback(WeakCallback, const void*) final {}
 
  private:
@@ -685,27 +680,20 @@
   graph_->AddEdge(parent_node, current_node);
 }
 
-void V8EmbedderGraphBuilder::VisitBackingStoreStrongly(
+void V8EmbedderGraphBuilder::VisitWeakContainer(
     const void* object,
-    const void* const* object_slot,
-    TraceDescriptor desc) {
-  if (!object)
-    return;
-  desc.callback(this, desc.base_object_payload);
-}
-
-void V8EmbedderGraphBuilder::VisitBackingStoreWeakly(
-    const void* object,
-    const void* const* object_slot,
+    const void* const* slot,
     TraceDescriptor strong_desc,
-    TraceDescriptor weak_desc,
-    WeakCallback,
-    const void*) {
+    TraceDescriptor ephemeron_iteration,
+    WeakCallback weak_callback,
+    const void* weak_callback_parameter) {
   // Only ephemerons have weak callbacks.
-  if (weak_desc.callback) {
+  if (ephemeron_iteration.callback) {
     // Heap snapshot is always run after a GC so we know there are no dead
     // entries in the backing store, thus it safe to trace it strongly.
-    VisitBackingStoreStrongly(object, object_slot, strong_desc);
+    if (object) {
+      Visit(object, strong_desc);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni
index 70247a4..e920250 100644
--- a/third_party/blink/renderer/bindings/generated_in_core.gni
+++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -276,8 +276,6 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_unparsed_value.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_variable_reference_value.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_variable_reference_value.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_viewport_rule.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_viewport_rule.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_custom_element_registry.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_custom_element_registry.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_custom_event.cc",
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index a383649..3ab344f 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -205,6 +205,26 @@
   return right_to_left_decl;
 }
 
+static CSSPropertyValueSet* MarkerUserAgentDeclarations() {
+  DEFINE_STATIC_LOCAL(
+      Persistent<MutableCSSPropertyValueSet>, marker_ua_decl,
+      (MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLQuirksMode)));
+  if (marker_ua_decl->IsEmpty()) {
+    // Set 'unicode-bidi: isolate'
+    marker_ua_decl->SetProperty(
+        CSSPropertyID::kUnicodeBidi,
+        *CSSIdentifierValue::Create(CSSValueID::kIsolate));
+
+    // Set 'font-variant-numeric: tabular-nums'
+    CSSValueList* variant_numeric = CSSValueList::CreateSpaceSeparated();
+    variant_numeric->Append(
+        *CSSIdentifierValue::Create(CSSValueID::kTabularNums));
+    marker_ua_decl->SetProperty(CSSPropertyID::kFontVariantNumeric,
+                                *variant_numeric);
+  }
+  return marker_ua_decl;
+}
+
 static void CollectScopedResolversForHostedShadowTrees(
     const Element& element,
     HeapVector<Member<ScopedStyleResolver>, 8>& resolvers) {
@@ -1110,20 +1130,8 @@
     // but that would use a slow universal element selector. So instead we apply
     // the styles here as an optimization.
     if (pseudo_style_request.pseudo_id == kPseudoIdMarker) {
-      auto* set = MakeGarbageCollected<MutableCSSPropertyValueSet>(
-          state.GetParserMode());
-
-      // Set 'unicode-bidi: isolate'
-      set->SetProperty(CSSPropertyID::kUnicodeBidi,
-                       *CSSIdentifierValue::Create(CSSValueID::kIsolate));
-
-      // Set 'font-variant-numeric: tabular-nums'
-      CSSValueList* variant_numeric = CSSValueList::CreateSpaceSeparated();
-      variant_numeric->Append(
-          *CSSIdentifierValue::Create(CSSValueID::kTabularNums));
-      set->SetProperty(CSSPropertyID::kFontVariantNumeric, *variant_numeric);
-
-      cascade.MutableMatchResult().AddMatchedProperties(set);
+      cascade.MutableMatchResult().AddMatchedProperties(
+          MarkerUserAgentDeclarations());
     }
 
     // TODO(obrufau): support styling nested pseudo-elements
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 2d919441..cfc73bf4 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -61,6 +61,7 @@
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "third_party/blink/public/platform/web_theme_engine.h"
+#include "third_party/blink/public/web/web_print_page_description.h"
 #include "third_party/blink/renderer/bindings/core/v8/html_script_element_or_svg_script_element.h"
 #include "third_party/blink/renderer/bindings/core/v8/isolated_world_csp.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
@@ -3033,16 +3034,12 @@
          EVisibility::kHidden;  // display property doesn't apply to @page.
 }
 
-void Document::PageSizeAndMarginsInPixels(int page_index,
-                                          DoubleSize& page_size,
-                                          int& margin_top,
-                                          int& margin_right,
-                                          int& margin_bottom,
-                                          int& margin_left) {
+void Document::GetPageDescription(int page_index,
+                                  WebPrintPageDescription* description) {
   scoped_refptr<const ComputedStyle> style = StyleForPage(page_index);
 
-  double width = page_size.Width();
-  double height = page_size.Height();
+  double width = description->size.Width();
+  double height = description->size.Height();
   switch (style->GetPageSizeType()) {
     case PageSizeType::kAuto:
       break;
@@ -3063,23 +3060,21 @@
     default:
       NOTREACHED();
   }
-  page_size = DoubleSize(width, height);
+  description->size = WebDoubleSize(width, height);
 
   // The percentage is calculated with respect to the width even for margin top
   // and bottom.
   // http://www.w3.org/TR/CSS2/box.html#margin-properties
-  margin_top = style->MarginTop().IsAuto()
-                   ? margin_top
-                   : IntValueForLength(style->MarginTop(), width);
-  margin_right = style->MarginRight().IsAuto()
-                     ? margin_right
-                     : IntValueForLength(style->MarginRight(), width);
-  margin_bottom = style->MarginBottom().IsAuto()
-                      ? margin_bottom
-                      : IntValueForLength(style->MarginBottom(), width);
-  margin_left = style->MarginLeft().IsAuto()
-                    ? margin_left
-                    : IntValueForLength(style->MarginLeft(), width);
+  if (!style->MarginTop().IsAuto())
+    description->margin_top = IntValueForLength(style->MarginTop(), width);
+  if (!style->MarginRight().IsAuto())
+    description->margin_right = IntValueForLength(style->MarginRight(), width);
+  if (!style->MarginBottom().IsAuto()) {
+    description->margin_bottom =
+        IntValueForLength(style->MarginBottom(), width);
+  }
+  if (!style->MarginLeft().IsAuto())
+    description->margin_left = IntValueForLength(style->MarginLeft(), width);
 }
 
 void Document::SetIsViewSource(bool is_view_source) {
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 028aa87..0a680ab 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -129,7 +129,6 @@
 class DocumentTimeline;
 class DocumentType;
 class DOMFeaturePolicy;
-class DoubleSize;
 class Element;
 class ElementDataCache;
 class ElementRegistrationOptions;
@@ -217,6 +216,7 @@
 struct FocusParams;
 struct IconURL;
 struct PhysicalOffset;
+struct WebPrintPageDescription;
 
 using MouseEventWithHitTestResults = EventWithHitTestResults<WebMouseEvent>;
 
@@ -644,16 +644,11 @@
   // Returns true if page box (margin boxes and page borders) is visible.
   bool IsPageBoxVisible(int page_index);
 
-  // Returns the preferred page size and margins in pixels, assuming 96
-  // pixels per inch. pageSize, marginTop, marginRight, marginBottom,
-  // marginLeft must be initialized to the default values that are used if
-  // auto is specified.
-  void PageSizeAndMarginsInPixels(int page_index,
-                                  DoubleSize& page_size,
-                                  int& margin_top,
-                                  int& margin_right,
-                                  int& margin_bottom,
-                                  int& margin_left);
+  // Gets the description for the specified page. This includes preferred page
+  // size and margins in pixels, assuming 96 pixels per inch. The size and
+  // margins must be initialized to the default values that are used if auto is
+  // specified.
+  void GetPageDescription(int page_index, WebPrintPageDescription*);
 
   ResourceFetcher* Fetcher() const { return fetcher_.Get(); }
 
diff --git a/third_party/blink/renderer/core/dom/document_test.cc b/third_party/blink/renderer/core/dom/document_test.cc
index c66420cb..e908501 100644
--- a/third_party/blink/renderer/core/dom/document_test.cc
+++ b/third_party/blink/renderer/core/dom/document_test.cc
@@ -41,6 +41,7 @@
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/common/feature_policy/document_policy_features.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom-blink.h"
+#include "third_party/blink/public/web/web_print_page_description.h"
 #include "third_party/blink/renderer/bindings/core/v8/isolated_world_csp.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
@@ -1232,14 +1233,14 @@
   GetDocument().GetFrame()->StartPrinting(initial_page_size, initial_page_size);
   GetDocument().View()->UpdateLifecyclePhasesForPrinting();
 
-  DoubleSize page_size;
-  int margin[4];
-  GetDocument().PageSizeAndMarginsInPixels(0, page_size, margin[0], margin[1],
-                                           margin[2], margin[3]);
+  WebPrintPageDescription description;
+  GetDocument().GetPageDescription(0, &description);
 
-  for (int side_margin : margin)
-    EXPECT_EQ(50, side_margin);
-  EXPECT_EQ(DoubleSize(400, 960), page_size);
+  EXPECT_EQ(50, description.margin_top);
+  EXPECT_EQ(50, description.margin_right);
+  EXPECT_EQ(50, description.margin_bottom);
+  EXPECT_EQ(50, description.margin_left);
+  EXPECT_EQ(WebDoubleSize(400, 960), description.size);
 }
 
 TEST(Document, HandlesDisconnectDuringHasTrustToken) {
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 6bc0899..2627293 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -1603,16 +1603,10 @@
   return GetFrame()->GetDocument()->StyleForPage(page_index)->GetPageSizeType();
 }
 
-void WebLocalFrameImpl::PageSizeAndMarginsInPixels(int page_index,
-                                                   WebDoubleSize& page_size,
-                                                   int& margin_top,
-                                                   int& margin_right,
-                                                   int& margin_bottom,
-                                                   int& margin_left) {
-  DoubleSize size = page_size;
-  GetFrame()->GetDocument()->PageSizeAndMarginsInPixels(
-      page_index, size, margin_top, margin_right, margin_bottom, margin_left);
-  page_size = size;
+void WebLocalFrameImpl::GetPageDescription(
+    int page_index,
+    WebPrintPageDescription* description) {
+  GetFrame()->GetDocument()->GetPageDescription(page_index, description);
 }
 
 void WebLocalFrameImpl::PrintPagesForTesting(
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index ea7b06c..66a95b485 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -140,12 +140,7 @@
                          const WebSourceLocation&) override;
   void SendOrientationChangeEvent() override;
   PageSizeType GetPageSizeType(int page_index) override;
-  void PageSizeAndMarginsInPixels(int page_index,
-                                  WebDoubleSize& page_size,
-                                  int& margin_top,
-                                  int& margin_right,
-                                  int& margin_bottom,
-                                  int& margin_left) override;
+  void GetPageDescription(int page_index, WebPrintPageDescription*) override;
   void ExecuteScript(const WebScriptSource&) override;
   void ExecuteScriptInIsolatedWorld(int32_t world_id,
                                     const WebScriptSource&) override;
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index 456e8a2..ef8bcbd2 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -520,6 +520,16 @@
   active_selection_end_ = option;
 }
 
+void HTMLSelectElement::ScrollToSelection() {
+  if (!IsFinishedParsingChildren())
+    return;
+  if (UsesMenuList())
+    return;
+  select_type_->ScrollToOption(ActiveSelectionEnd());
+  if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache())
+    cache->ListboxActiveIndexChanged(this);
+}
+
 const HTMLSelectElement::ListItems& HTMLSelectElement::GetListItems() const {
   if (should_recalc_list_items_) {
     RecalcListItems();
@@ -1153,8 +1163,10 @@
     SelectOption(option, flags);
   }
   option->SetDirty(true);
+  if (UsesMenuList())
+    return;
   select_type_->ListBoxOnChange();
-  select_type_->ScrollToSelection();
+  ScrollToSelection();
 }
 
 unsigned HTMLSelectElement::length() const {
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.h b/third_party/blink/renderer/core/html/forms/html_select_element.h
index 0360b9ef..eb28983 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.h
@@ -122,6 +122,8 @@
   Element* namedItem(const AtomicString& name);
   HTMLOptionElement* item(unsigned index);
 
+  void ScrollToSelection();
+
   bool CanSelectAll() const;
   void SelectAll();
   int ActiveSelectionEndListIndex() const;
diff --git a/third_party/blink/renderer/core/html/forms/select_type.cc b/third_party/blink/renderer/core/html/forms/select_type.cc
index bd7bc87e..11fda22f 100644
--- a/third_party/blink/renderer/core/html/forms/select_type.cc
+++ b/third_party/blink/renderer/core/html/forms/select_type.cc
@@ -620,7 +620,6 @@
   void DidBlur() override;
   void DidSetSuggestedOption(HTMLOptionElement* option) override;
   void SaveLastSelection() override;
-  void ScrollToSelection() override;
   void ScrollToOption(HTMLOptionElement* option) override;
   void SelectAll() override;
   void SaveListboxActiveSelection() override;
@@ -873,7 +872,7 @@
         }
         UpdateMultiSelectFocus();
       } else {
-        ScrollToSelection();
+        select_->ScrollToSelection();
       }
 
       return true;
@@ -944,15 +943,7 @@
                       is_in_non_contiguous_selection_;
     option->SetMultiSelectFocusedState(is_focused);
   }
-  ScrollToSelection();
-}
-
-void ListBoxSelectType::ScrollToSelection() {
-  if (!select_->IsFinishedParsingChildren())
-    return;
-  ScrollToOption(select_->ActiveSelectionEnd());
-  if (AXObjectCache* cache = select_->GetDocument().ExistingAXObjectCache())
-    cache->ListboxActiveIndexChanged(select_);
+  select_->ScrollToSelection();
 }
 
 void ListBoxSelectType::ScrollToOption(HTMLOptionElement* option) {
@@ -1122,7 +1113,7 @@
   UpdateMultiSelectFocus();
   select_->SetNeedsValidityCheck();
   if (scroll)
-    ScrollToSelection();
+    select_->ScrollToSelection();
   select_->NotifyFormStateChanged();
 }
 
@@ -1207,7 +1198,7 @@
 void SelectType::DidSelectOption(HTMLOptionElement*,
                                  HTMLSelectElement::SelectOptionFlags,
                                  bool) {
-  ScrollToSelection();
+  select_->ScrollToSelection();
   select_->SetNeedsValidityCheck();
 }
 
@@ -1233,8 +1224,6 @@
 
 void SelectType::MaximumOptionWidthMightBeChanged() const {}
 
-void SelectType::ScrollToSelection() {}
-
 void SelectType::ScrollToOption(HTMLOptionElement* option) {}
 
 void SelectType::SelectAll() {
diff --git a/third_party/blink/renderer/core/html/forms/select_type.h b/third_party/blink/renderer/core/html/forms/select_type.h
index d4b81d8..e6eb23d 100644
--- a/third_party/blink/renderer/core/html/forms/select_type.h
+++ b/third_party/blink/renderer/core/html/forms/select_type.h
@@ -46,7 +46,6 @@
   virtual const ComputedStyle* OptionStyle() const;
   virtual void MaximumOptionWidthMightBeChanged() const;
 
-  virtual void ScrollToSelection();
   virtual void ScrollToOption(HTMLOptionElement* option);
   virtual void SelectAll();
   virtual void SaveListboxActiveSelection();
diff --git a/third_party/blink/renderer/core/html/media/media_remoting_interstitial.h b/third_party/blink/renderer/core/html/media/media_remoting_interstitial.h
index c00cfa0..6457522 100644
--- a/third_party/blink/renderer/core/html/media/media_remoting_interstitial.h
+++ b/third_party/blink/renderer/core/html/media/media_remoting_interstitial.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_MEDIA_MEDIA_REMOTING_INTERSTITIAL_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_MEDIA_MEDIA_REMOTING_INTERSTITIAL_H_
 
-#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/core/html/html_div_element.h"
 #include "third_party/blink/renderer/platform/timer.h"
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index b7637ea..ef7c1ca 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -60,6 +60,7 @@
 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
+#include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
 #include "third_party/blink/renderer/core/dom/node.h"
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -860,6 +861,19 @@
   if (!node_id)
     return;
 
+  // First check whether focus-within was set because focus or focus-within was
+  // forced for a child node.
+  NodeIdToNumberFocusedChildren::iterator focused_it =
+      node_id_to_number_focused_children_.find(node_id);
+  unsigned focused_count =
+      focused_it == node_id_to_number_focused_children_.end()
+          ? 0
+          : focused_it->value;
+  if (pseudo_type == CSSSelector::kPseudoFocusWithin && focused_count > 0) {
+    *result = true;
+    return;
+  }
+
   NodeIdToForcedPseudoState::iterator it =
       node_id_to_forced_pseudo_state_.find(node_id);
   if (it == node_id_to_forced_pseudo_state_.end())
@@ -867,6 +881,7 @@
 
   bool force = false;
   unsigned forced_pseudo_state = it->value;
+
   switch (pseudo_type) {
     case CSSSelector::kPseudoActive:
       force = forced_pseudo_state & kPseudoActive;
@@ -1637,11 +1652,65 @@
     node_id_to_forced_pseudo_state_.Set(node_id, forced_pseudo_state);
   else
     node_id_to_forced_pseudo_state_.erase(node_id);
+
+  // When adding focus or focus-within, we force focus-within for ancestor
+  // nodes to emulate real focus for user convenience.
+
+  // Flips from no forced focus to the forced focus (:focus or :focus-within).
+  if (((forced_pseudo_state & kPseudoFocus) == kPseudoFocus &&
+       (current_forced_pseudo_state & kPseudoFocus) == 0) ||
+      ((forced_pseudo_state & kPseudoFocusWithin) == kPseudoFocusWithin &&
+       (current_forced_pseudo_state & kPseudoFocusWithin) == 0)) {
+    IncrementFocusedCountForAncestors(element);
+  }
+
+  // Flips from the forced focus (:focus or :focus-within) to no focus.
+  if (((forced_pseudo_state & kPseudoFocus) == 0 &&
+       (current_forced_pseudo_state & kPseudoFocus) == kPseudoFocus) ||
+      ((forced_pseudo_state & kPseudoFocusWithin) == 0 &&
+       (current_forced_pseudo_state & kPseudoFocusWithin) ==
+           kPseudoFocusWithin)) {
+    DecrementFocusedCountForAncestors(element);
+  }
+
   element->ownerDocument()->GetStyleEngine().MarkAllElementsForStyleRecalc(
       StyleChangeReasonForTracing::Create(style_change_reason::kInspector));
   return Response::Success();
 }
 
+void InspectorCSSAgent::IncrementFocusedCountForAncestors(Element* element) {
+  for (Node& ancestor : FlatTreeTraversal::AncestorsOf(*element)) {
+    int node_id = dom_agent_->BoundNodeId(&ancestor);
+    if (!node_id)
+      continue;
+    NodeIdToNumberFocusedChildren::iterator it =
+        node_id_to_number_focused_children_.find(node_id);
+    unsigned count =
+        it == node_id_to_number_focused_children_.end() ? 0 : it->value;
+    node_id_to_number_focused_children_.Set(node_id, count + 1);
+  }
+}
+
+void InspectorCSSAgent::DecrementFocusedCountForAncestors(Element* element) {
+  for (Node& ancestor : FlatTreeTraversal::AncestorsOf(*element)) {
+    int node_id = dom_agent_->BoundNodeId(&ancestor);
+    if (!node_id)
+      continue;
+    NodeIdToNumberFocusedChildren::iterator it =
+        node_id_to_number_focused_children_.find(node_id);
+    unsigned count =
+        it == node_id_to_number_focused_children_.end() ? 1 : it->value;
+    if (count <= 1) {
+      // If `count - 1` is zero or overflows, erase the node_id
+      // from the map to save memory. If there is zero focused child
+      // elements, :focus-within should not be forced.
+      node_id_to_number_focused_children_.erase(node_id);
+    } else {
+      node_id_to_number_focused_children_.Set(node_id, count - 1);
+    }
+  }
+}
+
 std::unique_ptr<protocol::CSS::CSSMedia> InspectorCSSAgent::BuildMediaObject(
     const MediaList* media,
     MediaListSource media_list_source,
@@ -2154,7 +2223,14 @@
       documents_to_change.insert(element->ownerDocument());
   }
 
+  for (auto& count : node_id_to_number_focused_children_) {
+    auto* element = To<Element>(dom_agent_->NodeForId(count.key));
+    if (element && element->ownerDocument())
+      documents_to_change.insert(element->ownerDocument());
+  }
+
   node_id_to_forced_pseudo_state_.clear();
+  node_id_to_number_focused_children_.clear();
   for (auto& document : documents_to_change) {
     document->GetStyleEngine().MarkAllElementsForStyleRecalc(
         StyleChangeReasonForTracing::Create(style_change_reason::kInspector));
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.h b/third_party/blink/renderer/core/inspector/inspector_css_agent.h
index 5815c86..449b85ea 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.h
@@ -245,6 +245,7 @@
       NodeToInspectorStyleSheet;  // bogus "stylesheets" with elements' inline
                                   // styles
   typedef HashMap<int, unsigned> NodeIdToForcedPseudoState;
+  typedef HashMap<int, unsigned> NodeIdToNumberFocusedChildren;
 
   void ResourceContentLoaded(std::unique_ptr<EnableCallback>);
   void CompleteEnabled();
@@ -303,6 +304,9 @@
 
   void ResetPseudoStates();
 
+  void IncrementFocusedCountForAncestors(Element*);
+  void DecrementFocusedCountForAncestors(Element*);
+
   Member<InspectorDOMAgent> dom_agent_;
   Member<InspectedFrames> inspected_frames_;
   Member<InspectorNetworkAgent> network_agent_;
@@ -322,6 +326,7 @@
 
   NodeToInspectorStyleSheet node_to_inspector_style_sheet_;
   NodeIdToForcedPseudoState node_id_to_forced_pseudo_state_;
+  NodeIdToNumberFocusedChildren node_id_to_number_focused_children_;
 
   Member<StyleRuleUsageTracker> tracker_;
 
diff --git a/third_party/blink/renderer/core/page/print_context.cc b/third_party/blink/renderer/core/page/print_context.cc
index f7f90db..6db4817 100644
--- a/third_party/blink/renderer/core/page/print_context.cc
+++ b/third_party/blink/renderer/core/page/print_context.cc
@@ -22,6 +22,7 @@
 
 #include <utility>
 
+#include "third_party/blink/public/web/web_print_page_description.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -286,15 +287,20 @@
                                                 int margin_right,
                                                 int margin_bottom,
                                                 int margin_left) {
-  DoubleSize page_size(width, height);
-  frame->GetDocument()->PageSizeAndMarginsInPixels(page_number, page_size,
-                                                   margin_top, margin_right,
-                                                   margin_bottom, margin_left);
+  WebPrintPageDescription description;
+  description.size = WebDoubleSize(width, height);
+  description.margin_top = margin_top;
+  description.margin_right = margin_right;
+  description.margin_bottom = margin_bottom;
+  description.margin_left = margin_left;
+  frame->GetDocument()->GetPageDescription(page_number, &description);
 
-  return "(" + String::Number(floor(page_size.Width())) + ", " +
-         String::Number(floor(page_size.Height())) + ") " +
-         String::Number(margin_top) + ' ' + String::Number(margin_right) + ' ' +
-         String::Number(margin_bottom) + ' ' + String::Number(margin_left);
+  return "(" + String::Number(floor(description.size.Width())) + ", " +
+         String::Number(floor(description.size.Height())) + ") " +
+         String::Number(description.margin_top) + ' ' +
+         String::Number(description.margin_right) + ' ' +
+         String::Number(description.margin_bottom) + ' ' +
+         String::Number(description.margin_left);
 }
 
 // static
diff --git a/third_party/blink/renderer/core/streams/writable_stream.h b/third_party/blink/renderer/core/streams/writable_stream.h
index 70533b4..99775e5 100644
--- a/third_party/blink/renderer/core/streams/writable_stream.h
+++ b/third_party/blink/renderer/core/streams/writable_stream.h
@@ -92,14 +92,6 @@
 
   // Inherited methods used internally.
 
-  // https://streams.spec.whatwg.org/#is-writable-stream-locked
-  // TODO(ricea): Delete this variant once the V8 extras implementation is
-  // removed.
-  base::Optional<bool> IsLocked(ScriptState*, ExceptionState&) const {
-    return IsLocked(this);
-  }
-
-  // This version can't fail.
   static bool IsLocked(const WritableStream* stream) { return stream->writer_; }
 
   void Serialize(ScriptState*, MessagePort*, ExceptionState&);
diff --git a/third_party/blink/renderer/core/streams/writable_stream_test.cc b/third_party/blink/renderer/core/streams/writable_stream_test.cc
index 731c0d3d..df68988 100644
--- a/third_party/blink/renderer/core/streams/writable_stream_test.cc
+++ b/third_party/blink/renderer/core/streams/writable_stream_test.cc
@@ -41,14 +41,10 @@
   ASSERT_TRUE(stream);
 
   EXPECT_FALSE(stream->locked());
-  EXPECT_EQ(stream->IsLocked(script_state, ASSERT_NO_EXCEPTION),
-            base::make_optional(false));
 
   stream->getWriter(script_state, ASSERT_NO_EXCEPTION);
 
   EXPECT_TRUE(stream->locked());
-  EXPECT_EQ(stream->IsLocked(script_state, ASSERT_NO_EXCEPTION),
-            base::make_optional(true));
 }
 
 TEST(WritableStreamTest, Serialize) {
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
index ea44f010..04477de 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
@@ -603,6 +603,7 @@
     blink::EnableTypingDetection(&apm_config, typing_detector_.get());
   }
 
+  apm_config.residual_echo_detector.enabled = false;
   audio_processing_->ApplyConfig(apm_config);
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_underlying_sink.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_underlying_sink.cc
index 4701116..814413870 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_underlying_sink.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_underlying_sink.cc
@@ -53,7 +53,14 @@
     return ScriptPromise();
   }
 
-  transformer_callback_.Run()->SendFrameToSink(std::move(webrtc_frame));
+  RTCEncodedAudioStreamTransformer* transformer = transformer_callback_.Run();
+  if (!transformer) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "No underlying sink");
+    return ScriptPromise();
+  }
+
+  transformer->SendFrameToSink(std::move(webrtc_frame));
   return ScriptPromise::CastUndefined(script_state);
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_underlying_sink_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_underlying_sink_test.cc
index a3fbf8c6..ad703c4bd 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_underlying_sink_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_underlying_sink_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_encoded_audio_frame.h"
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/core/streams/writable_stream_default_writer.h"
@@ -46,6 +47,17 @@
   uint32_t GetSsrc() const override { return 0; }
 };
 
+bool IsDOMException(ScriptState* script_state,
+                    ScriptValue value,
+                    DOMExceptionCode code) {
+  auto* dom_exception = V8DOMException::ToImplWithTypeCheck(
+      script_state->GetIsolate(), value.V8Value());
+  if (!dom_exception)
+    return false;
+
+  return dom_exception->code() == static_cast<uint16_t>(code);
+}
+
 }  // namespace
 
 class RTCEncodedAudioUnderlyingSinkTest : public testing::Test {
@@ -76,6 +88,14 @@
                            WTF::Unretained(this)));
   }
 
+  RTCEncodedAudioUnderlyingSink* CreateNullCallbackSink(
+      ScriptState* script_state) {
+    return MakeGarbageCollected<RTCEncodedAudioUnderlyingSink>(
+        script_state,
+        WTF::BindRepeating(
+            []() -> RTCEncodedAudioStreamTransformer* { return nullptr; }));
+  }
+
   RTCEncodedAudioStreamTransformer* GetTransformer() { return &transformer_; }
 
   ScriptValue CreateEncodedAudioFrameChunk(ScriptState* script_state) {
@@ -138,4 +158,25 @@
   EXPECT_TRUE(dummy_exception_state.HadException());
 }
 
+TEST_F(RTCEncodedAudioUnderlyingSinkTest, WriteToNullCallbackSinkFails) {
+  V8TestingScope v8_scope;
+  ScriptState* script_state = v8_scope.GetScriptState();
+  auto* sink = CreateNullCallbackSink(script_state);
+  auto* stream =
+      WritableStream::CreateWithCountQueueingStrategy(script_state, sink, 1u);
+
+  NonThrowableExceptionState exception_state;
+  auto* writer = stream->getWriter(script_state, exception_state);
+
+  EXPECT_CALL(*webrtc_callback_, OnTransformedFrame(_)).Times(0);
+  ScriptPromiseTester write_tester(
+      script_state,
+      writer->write(script_state, CreateEncodedAudioFrameChunk(script_state),
+                    exception_state));
+  write_tester.WaitUntilSettled();
+  EXPECT_TRUE(write_tester.IsRejected());
+  EXPECT_TRUE(IsDOMException(script_state, write_tester.Value(),
+                             DOMExceptionCode::kInvalidStateError));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink.cc
index d48c663..b4b7bd8 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink.cc
@@ -53,7 +53,14 @@
     return ScriptPromise();
   }
 
-  transformer_callback_.Run()->SendFrameToSink(std::move(webrtc_frame));
+  RTCEncodedVideoStreamTransformer* transformer = transformer_callback_.Run();
+  if (!transformer) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "No underlying sink");
+    return ScriptPromise();
+  }
+
+  transformer->SendFrameToSink(std::move(webrtc_frame));
   return ScriptPromise::CastUndefined(script_state);
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink_test.cc
index 7f5c84c..3a606d1 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_underlying_sink_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_encoded_video_frame.h"
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/core/streams/writable_stream_default_writer.h"
@@ -56,6 +57,17 @@
   uint32_t ssrc_;
 };
 
+bool IsDOMException(ScriptState* script_state,
+                    ScriptValue value,
+                    DOMExceptionCode code) {
+  auto* dom_exception = V8DOMException::ToImplWithTypeCheck(
+      script_state->GetIsolate(), value.V8Value());
+  if (!dom_exception)
+    return false;
+
+  return dom_exception->code() == static_cast<uint16_t>(code);
+}
+
 }  // namespace
 
 class RTCEncodedVideoUnderlyingSinkTest : public testing::Test {
@@ -86,6 +98,14 @@
                            WTF::Unretained(this)));
   }
 
+  RTCEncodedVideoUnderlyingSink* CreateNullCallbackSink(
+      ScriptState* script_state) {
+    return MakeGarbageCollected<RTCEncodedVideoUnderlyingSink>(
+        script_state,
+        WTF::BindRepeating(
+            []() -> RTCEncodedVideoStreamTransformer* { return nullptr; }));
+  }
+
   RTCEncodedVideoStreamTransformer* GetTransformer() { return &transformer_; }
 
   ScriptValue CreateEncodedVideoFrameChunk(ScriptState* script_state) {
@@ -148,4 +168,25 @@
   EXPECT_TRUE(dummy_exception_state.HadException());
 }
 
+TEST_F(RTCEncodedVideoUnderlyingSinkTest, WriteToNullCallbackSinkFails) {
+  V8TestingScope v8_scope;
+  ScriptState* script_state = v8_scope.GetScriptState();
+  auto* sink = CreateNullCallbackSink(script_state);
+  auto* stream =
+      WritableStream::CreateWithCountQueueingStrategy(script_state, sink, 1u);
+
+  NonThrowableExceptionState exception_state;
+  auto* writer = stream->getWriter(script_state, exception_state);
+
+  EXPECT_CALL(*webrtc_callback_, OnTransformedFrame(_)).Times(0);
+  ScriptPromiseTester write_tester(
+      script_state,
+      writer->write(script_state, CreateEncodedVideoFrameChunk(script_state),
+                    exception_state));
+  write_tester.WaitUntilSettled();
+  EXPECT_TRUE(write_tester.IsRejected());
+  EXPECT_TRUE(IsDOMException(script_state, write_tester.Value(),
+                             DOMExceptionCode::kInvalidStateError));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/heap_allocator.h b/third_party/blink/renderer/platform/heap/heap_allocator.h
index 298a96e2..f88fca7 100644
--- a/third_party/blink/renderer/platform/heap/heap_allocator.h
+++ b/third_party/blink/renderer/platform/heap/heap_allocator.h
@@ -234,19 +234,17 @@
   static void TraceVectorBacking(Visitor* visitor,
                                  const T* backing,
                                  const T* const* backing_slot) {
-    visitor->TraceBackingStoreStrongly(
-        reinterpret_cast<const HeapVectorBacking<T>*>(backing),
-        reinterpret_cast<const HeapVectorBacking<T>* const*>(backing_slot));
+    visitor->TraceMovablePointer(backing_slot);
+    visitor->Trace(reinterpret_cast<const HeapVectorBacking<T>*>(backing));
   }
 
   template <typename T, typename HashTable>
   static void TraceHashTableBackingStrongly(Visitor* visitor,
                                             const T* backing,
                                             const T* const* backing_slot) {
-    visitor->TraceBackingStoreStrongly(
-        reinterpret_cast<const HeapHashTableBacking<HashTable>*>(backing),
-        reinterpret_cast<const HeapHashTableBacking<HashTable>* const*>(
-            backing_slot));
+    visitor->TraceMovablePointer(backing_slot);
+    visitor->Trace(
+        reinterpret_cast<const HeapHashTableBacking<HashTable>*>(backing));
   }
 
   template <typename T, typename HashTable>
@@ -255,23 +253,18 @@
                                           const T* const* backing_slot,
                                           WeakCallback callback,
                                           const void* parameter) {
-    visitor->TraceBackingStoreWeakly<HashTable>(
+    visitor->TraceMovablePointer(backing_slot);
+    visitor->TraceWeakContainer(
         reinterpret_cast<const HeapHashTableBacking<HashTable>*>(backing),
         reinterpret_cast<const HeapHashTableBacking<HashTable>* const*>(
             backing_slot),
+        TraceTrait<HeapHashTableBacking<HashTable>>::GetTraceDescriptor(
+            backing),
+        TraceTrait<HeapHashTableBacking<HashTable>>::GetWeakTraceDescriptor(
+            backing),
         callback, parameter);
   }
 
-  template <typename T, typename HashTable>
-  static void TraceHashTableBackingOnly(Visitor* visitor,
-                                        const T* backing,
-                                        const T* const* backing_slot) {
-    visitor->TraceBackingStoreOnly(
-        reinterpret_cast<const HeapHashTableBacking<HashTable>*>(backing),
-        reinterpret_cast<const HeapHashTableBacking<HashTable>* const*>(
-            backing_slot));
-  }
-
  private:
   static Address MarkAsConstructed(Address address) {
     HeapObjectHeader::FromPayload(reinterpret_cast<void*>(address))
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index 85b9c1c0..b59e372 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -68,18 +68,6 @@
                  const void* object_weak_ref,
                  TraceDescriptor desc,
                  WeakCallback callback) final {}
-  void VisitBackingStoreStrongly(const void* object,
-                                 const void* const* object_slot,
-                                 TraceDescriptor desc) final {}
-  void VisitBackingStoreWeakly(const void*,
-                               const void* const*,
-                               TraceDescriptor,
-                               TraceDescriptor,
-                               WeakCallback,
-                               const void*) final {}
-  void VisitBackingStoreOnly(const void*, const void* const*) final {}
-  void RegisterBackingStoreCallback(const void* slot,
-                                    MovingObjectCallback) final {}
   void RegisterWeakCallback(WeakCallback, const void*) final {}
   void Visit(const TraceWrapperV8Reference<v8::Value>&) final {}
 
@@ -1541,7 +1529,7 @@
   driver.FinishGC();
 
   // Weak callback should register the slot.
-  EXPECT_EQ(driver.GetHeapCompactLastFixupCount(), 2u);
+  EXPECT_EQ(1u, driver.GetHeapCompactLastFixupCount());
 }
 
 TEST_F(IncrementalMarkingTest, ConservativeGCWhileCompactionScheduled) {
diff --git a/third_party/blink/renderer/platform/heap/marking_verifier.cc b/third_party/blink/renderer/platform/heap/marking_verifier.cc
index eb900719..45067dfe 100644
--- a/third_party/blink/renderer/platform/heap/marking_verifier.cc
+++ b/third_party/blink/renderer/platform/heap/marking_verifier.cc
@@ -37,24 +37,12 @@
   VerifyChild(object, desc.base_object_payload);
 }
 
-void MarkingVerifier::VisitBackingStoreStrongly(const void* object,
-                                                const void* const*,
-                                                TraceDescriptor desc) {
-  if (!object)
-    return;
-
-  // Contents of backing stores are verified through page iteration. The
-  // verification here only makes sure that the backing itself is properly
-  // marked.
-  VerifyChild(object, desc.base_object_payload);
-}
-
-void MarkingVerifier::VisitBackingStoreWeakly(const void* object,
-                                              const void* const*,
-                                              TraceDescriptor strong_desc,
-                                              TraceDescriptor weak_desc,
-                                              WeakCallback,
-                                              const void*) {
+void MarkingVerifier::VisitWeakContainer(const void* object,
+                                         const void* const*,
+                                         TraceDescriptor,
+                                         TraceDescriptor weak_desc,
+                                         WeakCallback,
+                                         const void*) {
   if (!object)
     return;
 
diff --git a/third_party/blink/renderer/platform/heap/marking_verifier.h b/third_party/blink/renderer/platform/heap/marking_verifier.h
index 07d12d74..0113d6a 100644
--- a/third_party/blink/renderer/platform/heap/marking_verifier.h
+++ b/third_party/blink/renderer/platform/heap/marking_verifier.h
@@ -25,20 +25,14 @@
                  TraceDescriptor desc,
                  WeakCallback callback) final;
 
-  void VisitBackingStoreStrongly(const void*,
-                                 const void* const*,
-                                 TraceDescriptor) final;
-
-  void VisitBackingStoreWeakly(const void*,
-                               const void* const*,
-                               TraceDescriptor,
-                               TraceDescriptor,
-                               WeakCallback,
-                               const void*) final;
+  void VisitWeakContainer(const void*,
+                          const void* const*,
+                          TraceDescriptor,
+                          TraceDescriptor,
+                          WeakCallback,
+                          const void*) final;
 
   // Unused overrides.
-  void VisitBackingStoreOnly(const void*, const void* const*) final {}
-  void RegisterBackingStoreCallback(const void*, MovingObjectCallback) final {}
   void RegisterWeakCallback(WeakCallback, const void*) final {}
   void Visit(const TraceWrapperV8Reference<v8::Value>&) final {}
 
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.cc b/third_party/blink/renderer/platform/heap/marking_visitor.cc
index 1679e23..fef919a 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -40,15 +40,6 @@
   weak_callback_worklist_.Push({callback, object});
 }
 
-void MarkingVisitorCommon::RegisterBackingStoreReference(
-    const void* const* slot) {
-  if (marking_mode_ != kGlobalMarkingWithCompaction)
-    return;
-  if (Heap().ShouldRegisterMovingAddress()) {
-    movable_reference_worklist_.Push(slot);
-  }
-}
-
 void MarkingVisitorCommon::RegisterBackingStoreCallback(
     const void* backing,
     MovingObjectCallback callback) {
@@ -59,6 +50,14 @@
   }
 }
 
+void MarkingVisitorCommon::RegisterMovableSlot(const void* const* slot) {
+  if (marking_mode_ != kGlobalMarkingWithCompaction)
+    return;
+  if (Heap().ShouldRegisterMovingAddress()) {
+    movable_reference_worklist_.Push(slot);
+  }
+}
+
 void MarkingVisitorCommon::VisitWeak(const void* object,
                                      const void* object_weak_ref,
                                      TraceDescriptor desc,
@@ -73,39 +72,6 @@
   RegisterWeakCallback(callback, object_weak_ref);
 }
 
-void MarkingVisitorCommon::VisitBackingStoreStrongly(
-    const void* object,
-    const void* const* object_slot,
-    TraceDescriptor desc) {
-  RegisterBackingStoreReference(object_slot);
-  if (!object)
-    return;
-  Visit(object, desc);
-}
-
-// All work is registered through RegisterWeakCallback.
-void MarkingVisitorCommon::VisitBackingStoreWeakly(
-    const void* object,
-    const void* const* object_slot,
-    TraceDescriptor strong_desc,
-    TraceDescriptor weak_desc,
-    WeakCallback weak_callback,
-    const void* weak_callback_parameter) {
-  RegisterBackingStoreReference(object_slot);
-
-  // In case there's no object present, weakness processing is omitted. The GC
-  // relies on the fact that in such cases touching the weak data structure will
-  // strongify its references.
-  if (!object)
-    return;
-
-  // Register final weak processing of the backing store.
-  RegisterWeakCallback(weak_callback, weak_callback_parameter);
-  // Register ephemeron callbacks if necessary.
-  if (weak_desc.callback)
-    weak_table_worklist_.Push(weak_desc);
-}
-
 void MarkingVisitorCommon::VisitEphemeron(const void* key,
                                           const void* value,
                                           TraceCallback value_trace_callback) {
@@ -115,15 +81,37 @@
   value_trace_callback(this, value);
 }
 
-void MarkingVisitorCommon::VisitBackingStoreOnly(
+void MarkingVisitorCommon::VisitWeakContainer(
     const void* object,
-    const void* const* object_slot) {
-  RegisterBackingStoreReference(object_slot);
+    const void* const*,
+    TraceDescriptor,
+    TraceDescriptor weak_desc,
+    WeakCallback weak_callback,
+    const void* weak_callback_parameter) {
+  // In case there's no object present, weakness processing is omitted. The GC
+  // relies on the fact that in such cases touching the weak data structure will
+  // strongify its references.
   if (!object)
     return;
+
+  // Only trace the container initially. Its buckets will be processed after
+  // marking. The interesting cases  are:
+  // - The backing of the container is dropped using clear(): The backing can
+  //   still be compacted but empty/deleted buckets will only be destroyed once
+  //   the backing is reclaimed by the garbage collector on the next cycle.
+  // - The container expands/shrinks: Buckets are moved to the new backing
+  //   store and strongified, resulting in all buckets being alive. The old
+  //   backing store is marked but only contains empty/deleted buckets as all
+  //   non-empty/deleted buckets have been moved to the new backing store.
   HeapObjectHeader* header = HeapObjectHeader::FromPayload(object);
   MarkHeaderNoTracing(header);
   AccountMarkedBytes(header);
+
+  // Register final weak processing of the backing store.
+  RegisterWeakCallback(weak_callback, weak_callback_parameter);
+  // Register ephemeron callbacks if necessary.
+  if (weak_desc.callback)
+    weak_table_worklist_.Push(weak_desc);
 }
 
 // static
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.h b/third_party/blink/renderer/platform/heap/marking_visitor.h
index a128bae..af99c8f 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.h
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.h
@@ -33,22 +33,14 @@
   };
 
   void VisitWeak(const void*, const void*, TraceDescriptor, WeakCallback) final;
-  void VisitBackingStoreStrongly(const void*,
-                                 const void* const*,
-                                 TraceDescriptor) final;
-  void VisitBackingStoreWeakly(const void*,
-                               const void* const*,
-                               TraceDescriptor,
-                               TraceDescriptor,
-                               WeakCallback,
-                               const void*) final;
+  void VisitWeakContainer(const void*,
+                          const void* const*,
+                          TraceDescriptor,
+                          TraceDescriptor,
+                          WeakCallback,
+                          const void*) final;
   void VisitEphemeron(const void*, const void*, TraceCallback) final;
 
-  // Used to only mark the backing store when it has been registered for weak
-  // processing. In this case, the contents are processed separately using
-  // the corresponding traits but the backing store requires marking.
-  void VisitBackingStoreOnly(const void*, const void* const*) final;
-
   // This callback mechanism is needed to account for backing store objects
   // containing intra-object pointers, all of which must be relocated/rebased
   // with respect to the moved-to location.
@@ -56,6 +48,9 @@
   // For Blink, |HeapLinkedHashSet<>| is currently the only abstraction which
   // relies on this feature.
   void RegisterBackingStoreCallback(const void*, MovingObjectCallback) final;
+
+  void RegisterMovableSlot(const void* const*) final;
+
   void RegisterWeakCallback(WeakCallback, const void*) final;
 
   // Flush private segments remaining in visitor's worklists to global pools.
@@ -78,8 +73,6 @@
   // marked upon calling.
   bool MarkHeaderNoTracing(HeapObjectHeader*);
 
-  void RegisterBackingStoreReference(const void* const* slot);
-
   MarkingWorklist::View marking_worklist_;
   WriteBarrierWorklist::View write_barrier_worklist_;
   NotFullyConstructedWorklist::View not_fully_constructed_worklist_;
diff --git a/third_party/blink/renderer/platform/heap/visitor.h b/third_party/blink/renderer/platform/heap/visitor.h
index ba5f092..b5db7048 100644
--- a/third_party/blink/renderer/platform/heap/visitor.h
+++ b/third_party/blink/renderer/platform/heap/visitor.h
@@ -155,45 +155,6 @@
     Visit(t, TraceDescriptorFor(t));
   }
 
-  template <typename T>
-  void TraceBackingStoreStrongly(const T* backing_store,
-                                 const T* const* backing_store_slot) {
-    static_assert(sizeof(T), "T must be fully defined");
-    static_assert(IsGarbageCollectedType<T>::value,
-                  "T needs to be a garbage collected object");
-
-    VisitBackingStoreStrongly(
-        backing_store, reinterpret_cast<const void* const*>(backing_store_slot),
-        TraceDescriptorFor(backing_store));
-  }
-
-  template <typename HashTable, typename T>
-  void TraceBackingStoreWeakly(const T* backing_store,
-                               const T* const* backing_store_slot,
-                               WeakCallback weak_callback,
-                               const void* weak_callback_parameter) {
-    static_assert(sizeof(T), "T must be fully defined");
-    static_assert(IsGarbageCollectedType<T>::value,
-                  "T needs to be a garbage collected object");
-
-    VisitBackingStoreWeakly(
-        backing_store, reinterpret_cast<const void* const*>(backing_store_slot),
-        TraceDescriptorFor(backing_store),
-        WeakTraceDescriptorFor(backing_store), weak_callback,
-        weak_callback_parameter);
-  }
-
-  template <typename T>
-  void TraceBackingStoreOnly(const T* backing_store,
-                             const T* const* backing_store_slot) {
-    static_assert(sizeof(T), "T must be fully defined");
-    static_assert(IsGarbageCollectedType<T>::value,
-                  "T needs to be a garbage collected object");
-
-    VisitBackingStoreOnly(backing_store, reinterpret_cast<const void* const*>(
-                                             backing_store_slot));
-  }
-
   // WeakMember version of the templated trace method. It doesn't keep
   // the traced thing alive, but will write null to the WeakMember later
   // if the pointed-to object is dead. It's lying for this to be const,
@@ -246,6 +207,26 @@
                    value, value_trace_callback);
   }
 
+  template <typename T>
+  void TraceWeakContainer(const T* object,
+                          const T* const* slot,
+                          TraceDescriptor strong_desc,
+                          TraceDescriptor weak_dec,
+                          WeakCallback weak_callback,
+                          const void* weak_callback_parameter) {
+    static_assert(sizeof(T), "T must be fully defined");
+    static_assert(IsGarbageCollectedType<T>::value,
+                  "T needs to be a garbage collected object");
+    VisitWeakContainer(reinterpret_cast<const void*>(object),
+                       reinterpret_cast<const void* const*>(slot), strong_desc,
+                       weak_dec, weak_callback, weak_callback_parameter);
+  }
+
+  template <typename T>
+  void TraceMovablePointer(const T* const* slot) {
+    RegisterMovableSlot(reinterpret_cast<const void* const*>(slot));
+  }
+
   // Registers an instance method using |RegisterWeakCallback|. See description
   // below.
   template <typename T, void (T::*method)(const LivenessBroker&)>
@@ -278,22 +259,25 @@
                          TraceDescriptor,
                          WeakCallback) = 0;
 
-  // Visitors for collection backing stores.
-  virtual void VisitBackingStoreStrongly(const void*,
-                                         const void* const*,
-                                         TraceDescriptor) = 0;
-  virtual void VisitBackingStoreWeakly(const void*,
-                                       const void* const*,
-                                       TraceDescriptor,
-                                       TraceDescriptor,
-                                       WeakCallback,
-                                       const void*) = 0;
-  virtual void VisitBackingStoreOnly(const void*, const void* const*) = 0;
-
   // Visits ephemeron pairs which are a combination of weak and strong keys and
   // values.
   virtual void VisitEphemeron(const void*, const void*, TraceCallback) {}
 
+  // Visits a container |object| holding ephemeron pairs held from |slot|.  The
+  // descriptor |strong_desc| can be used to enforce strong treatment of
+  // |object|. The |weak_desc| descriptor is invoked repeatedly until no
+  // more new objects are found. It is expected that |weak_desc| processing
+  // ultimately yields in a call to VisitEphemeron. After marking all reachable
+  // objects, |weak_callback| is invoked with |weak_callback_parameter|. It is
+  // expected that this callback is used to reset non-live entries in the
+  // ephemeron container.
+  virtual void VisitWeakContainer(const void* object,
+                                  const void* const* slot,
+                                  TraceDescriptor strong_desc,
+                                  TraceDescriptor weak_desc,
+                                  WeakCallback weak_callback,
+                                  const void* weak_callback_parameter) {}
+
   // Visits cross-component references to V8.
 
   virtual void Visit(const TraceWrapperV8Reference<v8::Value>&) = 0;
@@ -301,7 +285,9 @@
   // Registers backing store pointers so that they can be moved and properly
   // updated.
   virtual void RegisterBackingStoreCallback(const void* backing,
-                                            MovingObjectCallback) = 0;
+                                            MovingObjectCallback) {}
+
+  virtual void RegisterMovableSlot(const void* const* slot) {}
 
   // Adds a |callback| that is invoked with |parameter| after liveness has been
   // computed on the whole object graph. The |callback| may use the provided
@@ -333,11 +319,6 @@
     return TraceTrait<T>::GetTraceDescriptor(traceable);
   }
 
-  template <typename T>
-  static inline TraceDescriptor WeakTraceDescriptorFor(const T* traceable) {
-    return TraceTrait<T>::GetWeakTraceDescriptor(traceable);
-  }
-
  private:
   template <typename T>
   static void HandleWeakCell(const LivenessBroker&, const void*);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 5a4d13f..64c3d17 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -349,9 +349,7 @@
     },
     {
       name: "ContactsManagerExtraProperties",
-      origin_trial_feature_name: "ContactsManagerExtraProperties",
-      origin_trial_os: ["android"],
-      status: {"Android": "experimental", "default": "test"},
+      status:  {"Android": "stable", "default": "test"},
     },
     {
       name: "ContentIndex",
diff --git a/third_party/blink/renderer/platform/wtf/hash_table.h b/third_party/blink/renderer/platform/wtf/hash_table.h
index 5a4468d..2ad6f20b 100644
--- a/third_party/blink/renderer/platform/wtf/hash_table.h
+++ b/third_party/blink/renderer/platform/wtf/hash_table.h
@@ -2174,17 +2174,6 @@
     // Weak HashTable. The HashTable may be held alive strongly from somewhere
     // else, e.g., an iterator.
 
-    // Only trace the backing store. Its buckets will be processed after
-    // marking. The interesting cases for marking are:
-    // - The backing is dropped using clear(): The backing can still be
-    //   compacted but empty/deleted buckets will only be destroyed once the
-    //   backing is reclaimed by the garbage collector on the next cycle.
-    // - The hash table expands/shrinks: Buckets are moved to the new backing
-    //   store and strongified, resulting in all buckets being alive. The old
-    //   backing store is marked but only contains empty/deleted buckets as all
-    //   non-empty/deleted buckets have been moved to the new backing store.
-    Allocator::template TraceHashTableBackingOnly<ValueType, HashTable>(
-        visitor, table, &table_);
     // Trace the table weakly. For marking this will result in delaying the
     // processing until the end of the atomic pause. It is safe to trace
     // weakly multiple times.
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index a5a5556..5457919 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -1979,11 +1979,6 @@
 [ Linux ] virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/web-nfc-origin-trial-interfaces.html [ Skip ]
 [ Mac ] virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/web-nfc-origin-trial-interfaces.html [ Skip ]
 
-# Chrome Win/Linux/Mac don't support Contacts Picker API yet.
-[ Win ] virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/contacts-manager-extra-properties-trial-interfaces.html [ Skip ]
-[ Linux ] virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/contacts-manager-extra-properties-trial-interfaces.html [ Skip ]
-[ Mac ] virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/contacts-manager-extra-properties-trial-interfaces.html [ Skip ]
-
 crbug.com/626703 [ Win7 ] external/wpt/pointerevents/pointerevent_fractional_coordinates-manual.html [ Skip ]
 
 # These tests require a preferred dark color-scheme and are only run as virtual tests
diff --git a/third_party/blink/web_tests/editing/selection/caret-ltr-2-left.html b/third_party/blink/web_tests/editing/selection/caret-ltr-2-left.html
deleted file mode 100644
index a308ee2..0000000
--- a/third_party/blink/web_tests/editing/selection/caret-ltr-2-left.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<html>
-<body>
-<p>
-This tests that clicking in a contenteditable div will set the caret in the right position for LTR text in a RTL block.
-To test manually, click the left of the text. The caret should be on the left edge.
-</p>
-<div style="direction: rtl; font-size: 20px; width: 20ex; border: solid thin black; padding: 10px;" contenteditable>WebKit2</div>
-<script src="resources/caret-edge-shared.js"></script>
-<script>
-
-var clickOn = 'left';
-var expectedOffset = 7;
-if (window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled) {
-  // When bidi caret affinity is enabled, carets are shown at different visual
-  // locations. Use the changed test expectations in this case.
-  expectedOffset = 0;
-}
-
-runTest();
-
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/editing/selection/caret-ltr-2.html b/third_party/blink/web_tests/editing/selection/caret-ltr-2.html
index 13e52e9a..06c8716 100644
--- a/third_party/blink/web_tests/editing/selection/caret-ltr-2.html
+++ b/third_party/blink/web_tests/editing/selection/caret-ltr-2.html
@@ -1,24 +1,52 @@
-<!DOCTYPE html>
-<html>
-<body>
-<p>
-This tests that clicking in a contenteditable div will set the caret in the right position for LTR text in a RTL block.
-To test manually, click the right of the text. The caret should be on the right edge.
-</p>
-<div style="direction: rtl; font-size: 20px; width: 20ex; border: solid thin black; padding: 10px;" contenteditable>WebKit2</div>
-<script src="resources/caret-edge-shared.js"></script>
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
 <script>
+const bidiCaretAffinityEnabled =
+    window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
 
-var clickOn = 'right';
-var expectedOffset = 0;
-if (window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled) {
-  // When bidi caret affinity is enabled, carets are shown at different visual
-  // locations. Use the changed test expectations in this case.
-  expectedOffset = 7;
-}
+selection_test(
+    [
+        '<div contenteditable id="target" style="direction: rtl">',
+            'abc',
+        '</div>',
+    ],
+    selection => {
+        if (!window.eventSender)
+          throw 'This test requires eventSender.';
+        const target = selection.document.getElementById('target');
+        eventSender.mouseMoveTo(
+            selection.computeLeft(target) + 3,
+            selection.computeLeft(target) + 3);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+    },
+    [
+        '<div contenteditable id="target" style="direction: rtl">',
+            bidiCaretAffinityEnabled ? '|abc' : 'abc|',
+        '</div>',
+    ], 'Click left');
 
-runTest();
-
+selection_test(
+    [
+        '<div contenteditable id="target" style="direction: rtl">',
+            'abc',
+        '</div>',
+    ],
+    selection => {
+        if (!window.eventSender)
+          throw 'This test requires eventSender.';
+        const target = selection.document.getElementById('target');
+        eventSender.mouseMoveTo(
+            selection.computeLeft(target) + target.offsetWidth - 3,
+            selection.computeLeft(target) + 3);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+    },
+    [
+        '<div contenteditable id="target" style="direction: rtl">',
+            bidiCaretAffinityEnabled ? 'abc|' : '|abc',
+        '</div>',
+    ], 'Click right');
 </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/editing/selection/caret-rtl-2-left.html b/third_party/blink/web_tests/editing/selection/caret-rtl-2-left.html
deleted file mode 100644
index b140353..0000000
--- a/third_party/blink/web_tests/editing/selection/caret-rtl-2-left.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<html>
-<body>
-<p>
-This tests that clicking in a contenteditable div will set the caret in the right position for RTL text in a LTR block.
-To test manually, click the left of the text. The caret should be on the left edge.
-</p>
-<div style="font-size: 20px; width: 20ex; border: solid thin black; padding: 10px;" contenteditable>&#x05e9;&#x05d3;&#x05d4; &#x05d1;&#x05d5;&#x05e8;</div>
-<script src="resources/caret-edge-shared.js"></script>
-<script>
-
-var clickOn = 'left';
-var expectedOffset = 0;
-if (window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled) {
-  // When bidi caret affinity is enabled, carets are shown at different visual
-  // locations. Use the changed test expectations in this case.
-  expectedOffset = 7;
-}
-
-runTest();
-
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/editing/selection/caret-rtl-2.html b/third_party/blink/web_tests/editing/selection/caret-rtl-2.html
index ebd2a2e..fe64760 100644
--- a/third_party/blink/web_tests/editing/selection/caret-rtl-2.html
+++ b/third_party/blink/web_tests/editing/selection/caret-rtl-2.html
@@ -1,24 +1,55 @@
-<!DOCTYPE html>
-<html>
-<body>
-<p>
-This tests that clicking in a contenteditable div will set the caret in the right position for RTL text in a RTL block.
-To test manually, click the right of the text. The caret should be on the right edge.
-</p>
-<div style="font-size: 20px; width: 20ex; border: solid thin black; padding: 10px;" contenteditable>&#x05e9;&#x05d3;&#x05d4; &#x05d1;&#x05d5;&#x05e8;</div>
-<script src="resources/caret-edge-shared.js"></script>
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
 <script>
+const bidiCaretAffinityEnabled =
+    window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
 
-var clickOn = 'right';
-var expectedOffset = 7;
-if (window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled) {
-  // When bidi caret affinity is enabled, carets are shown at different visual
-  // locations. Use the changed test expectations in this case.
-  expectedOffset = 0;
-}
-
-runTest();
-
+selection_test(
+    [
+        '<div contenteditable id="target">',
+            '\u05e9;\u05d3;\u05d4; \u05d1;\u05d5;\u05e8;',
+        '</div>',
+    ],
+    selection => {
+        if (!window.eventSender)
+          throw 'This test requires eventSender.';
+        const target = selection.document.getElementById('target');
+        eventSender.mouseMoveTo(
+            selection.computeLeft(target) + 3,
+            selection.computeLeft(target) + 3);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+    },
+    [
+        '<div contenteditable id="target">',
+            bidiCaretAffinityEnabled
+              ? '\u05e9;\u05d3;\u05d4; \u05d1;\u05d5;\u05e8;|'
+              : '|\u05e9;\u05d3;\u05d4; \u05d1;\u05d5;\u05e8;',
+        '</div>',
+    ], 'Click left');
+selection_test(
+    [
+        '<div contenteditable id="target">',
+            '\u05e9;\u05d3;\u05d4; \u05d1;\u05d5;\u05e8;',
+        '</div>',
+    ],
+    selection => {
+        if (!window.eventSender)
+          throw 'This test requires eventSender.';
+        const target = selection.document.getElementById('target');
+        eventSender.mouseMoveTo(
+            selection.computeLeft(target) + target.offsetWidth - 3,
+            selection.computeLeft(target) + 3);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+    },
+    [
+        '<div contenteditable id="target">',
+            bidiCaretAffinityEnabled
+              ? '|\u05e9;\u05d3;\u05d4; \u05d1;\u05d5;\u05e8;'
+              : '\u05e9;\u05d3;\u05d4; \u05d1;\u05d5;\u05e8;|',
+        '</div>',
+    ], 'Click right');
 </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/editing/selection/caret-rtl-right.html b/third_party/blink/web_tests/editing/selection/caret-rtl-right.html
deleted file mode 100644
index 49165f2..0000000
--- a/third_party/blink/web_tests/editing/selection/caret-rtl-right.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html>
-<html>
-<body>
-<p>
-This tests that clicking in a contenteditable div will set the caret in the right position for RTL text in a RTL block.
-To test manually, click the right of the text. The caret should be on the right edge.
-</p>
-<div style="direction: rtl; font-size: 20px; width: 20ex; border: solid thin black; padding: 10px;" contenteditable>&#x05e9;&#x05d3;&#x05d4; &#x05d1;&#x05d5;&#x05e8;</div>
-<script src="resources/caret-edge-shared.js"></script>
-<script>
-
-var clickOn = 'right';
-var expectedOffset = 0;
-
-runTest();
-
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/editing/selection/caret-rtl.html b/third_party/blink/web_tests/editing/selection/caret-rtl.html
index 5424545..e807b2d 100644
--- a/third_party/blink/web_tests/editing/selection/caret-rtl.html
+++ b/third_party/blink/web_tests/editing/selection/caret-rtl.html
@@ -1,19 +1,50 @@
-<!DOCTYPE html>
-<html>
-<body>
-<p>
-This tests that clicking in a contenteditable div will set the caret in the right position for RTL text in a RTL block.
-To test manually, click the left of the text. The caret should be on the left edge.
-</p>
-<div style="direction: rtl; font-size: 20px; width: 20ex; border: solid thin black; padding: 10px;" contenteditable>&#x05e9;&#x05d3;&#x05d4; &#x05d1;&#x05d5;&#x05e8;</div>
-<script src="resources/caret-edge-shared.js"></script>
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
 <script>
-
-var clickOn = 'left';
-var expectedOffset = 7;
-
-runTest();
-
+selection_test(
+    [
+        '<div contenteditable dir="rtl" id="target">',
+            '\u05e9;\u05d3;\u05d4; \u05d1;\u05d5;\u05e8;',
+        '</div>',
+    ],
+    selection => {
+        if (!window.eventSender)
+          throw 'This test requires eventSender.';
+        const target = selection.document.getElementById('target');
+        eventSender.mouseMoveTo(
+            selection.computeLeft(target) + 3,
+            selection.computeLeft(target) + 3);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+    },
+    [
+        '<div contenteditable dir="rtl" id="target">',
+            '\u05e9;\u05d3;\u05d4; \u05d1;\u05d5;\u05e8;|',
+        '</div>',
+    ],
+    'Click left');
+selection_test(
+    [
+        '<div contenteditable dir="rtl" id="target">',
+            '\u05e9;\u05d3;\u05d4; \u05d1;\u05d5;\u05e8;',
+        '</div>',
+    ],
+    selection => {
+        if (!window.eventSender)
+          throw 'This test requires eventSender.';
+        const target = selection.document.getElementById('target');
+        eventSender.mouseMoveTo(
+            selection.computeLeft(target) + target.offsetWidth - 3,
+            selection.computeLeft(target) + 3);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+    },
+    [
+        '<div contenteditable dir="rtl" id="target">',
+            '|\u05e9;\u05d3;\u05d4; \u05d1;\u05d5;\u05e8;',
+        '</div>',
+    ],
+    'Click right');
 </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 6acb1a1..6a5b80cc0 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -78626,7 +78626,7 @@
       ]
      ],
      "ruby-reflow-001-opaqueruby.html": [
-      "fe4a80423839f6167fa0e42315b6b814cc839b92",
+      "77feb2e6b8dcdd514114dbec9867529e3bda0323",
       [
        null,
        [
@@ -78639,7 +78639,7 @@
       ]
      ],
      "ruby-reflow-001-transparentruby.html": [
-      "3e7112c4d61397e74cd1d75519292f3ecca5e9ee",
+      "008c5250bc364d4a9e15643ce38dc4d18eb7a145",
       [
        null,
        [
@@ -78665,7 +78665,7 @@
       ]
      ],
      "ruby-whitespace-001.html": [
-      "9df61585700777911c19e8336beed37827ce78d4",
+      "9072a8e6841925a5023730ad48b5a2f074b1d0dd",
       [
        null,
        [
@@ -78678,7 +78678,7 @@
       ]
      ],
      "ruby-whitespace-002.html": [
-      "bc84919a123c0554d9a6f80bea7d78341eb6cafe",
+      "98ed78913375ad36d2422ca231cb880a07032741",
       [
        null,
        [
@@ -152835,10 +152835,6 @@
       "b7affbe7d41dd9e7bf555aa151cf6d31fb2279a9",
       []
      ],
-     "post-redirect-stacktrace.https-expected.txt": [
-      "2eda089f8375b53dcb928bb0ebf8ff46ba270868",
-      []
-     ],
      "post-redirect-stacktrace.https.html.headers": [
       "644ed867f3303048120bc72e4ca5a5ede3fa6d3b",
       []
@@ -153526,6 +153522,14 @@
      "b8a1d0a6098492b615dfae8a1e5e44ade8a2f3db",
      []
     ],
+    "idlharness.tentative.https.any.sharedworker-expected.txt": [
+     "ad18929256152fa1478c17ef8acb9acfbdaf8096",
+     []
+    ],
+    "idlharness.tentative.https.any.worker-expected.txt": [
+     "ad18929256152fa1478c17ef8acb9acfbdaf8096",
+     []
+    ],
     "resources": {
      "always_changing_sw.sub.js": [
       "9fdf99848fa50316e275cd6636a5755270a9bb1e",
@@ -175578,11 +175582,11 @@
       []
      ],
      "ruby-whitespace-001-ref.html": [
-      "d24e207f8676d010cdfef2ddac1412833944faaf",
+      "04fa59e6ba038e03a13c8101fcbb6ea39561eed8",
       []
      ],
      "ruby-whitespace-002-ref.html": [
-      "8aade20051630ee89d258916b1029a71d91ce0fb",
+      "2b22891738cb1a776e411857b33a7a993c120363",
       []
      ],
      "support": {
@@ -198792,7 +198796,11 @@
       []
      ],
      "cache-storage-reporting-dedicated-worker.https-expected.txt": [
-      "020757c74f1d8efd65818362c5529f4f0377b6b9",
+      "f305a39c1e214f9e9c968998b50c327b288c0fec",
+      []
+     ],
+     "cache-storage-reporting-shared-worker.https-expected.txt": [
+      "643200226b30cbe10b5b55f74162c1e7129a52bf",
       []
      ],
      "data.https.html.headers": [
@@ -198831,6 +198839,10 @@
       "289659a41fdf41178781c764643f8946f4ec09b7",
       []
      ],
+     "reporting-subresource-corp.https-expected.txt": [
+      "62ff46ed2ac3185f83e4a9dd6ba1c3d3206a2511",
+      []
+     ],
      "reporting.https-expected.txt": [
       "59458db4e4866cab0622b22614c82421bc71a1c7",
       []
@@ -198869,7 +198881,7 @@
        []
       ],
       "cache-storage-reporting.js": [
-       "af36ccf9d3df852f75af46339e7df6bff23a5402",
+       "86dff9c8459003b7ceeb664ebbac920518505db3",
        []
       ],
       "coep-frame.html": [
@@ -198940,6 +198952,10 @@
        "b7c8b304178b4f4aa213703d6870e84267acba6c",
        []
       ],
+      "reporting-worker.js": [
+       "0f8a2ce4c87da7c0721b3a3d18af0d231fbacda2",
+       []
+      ],
       "require-corp-sw-import-scripts.js": [
        "e652c5bf303074d7c396f53146bb8af6c3f35644",
        []
@@ -205567,6 +205583,10 @@
         "4f7c4f2cb3497108feba281dc2bf5f276c8372fd",
         []
        ],
+       "image-loading-lazy-referrerpolicy-change.sub-expected.txt": [
+        "aa09bf93e1326d379228a5334b59fe1b7f06d35c",
+        []
+       ],
        "image-loading-lazy-slow-aspect-ratio-ref.html": [
         "6de01a9b3bbf19567555af2524f8950abdfb2d81",
         []
@@ -208480,10 +208500,6 @@
        "navigatorcookies-cookieenabled-false-manual-expected.txt": [
         "a95972302bfe24a2e207a7085bde0d103426de8f",
         []
-       ],
-       "protocol.https-expected.txt": [
-        "de0d8507d99a8e73d7eacd090e7c2633c99c2207",
-        []
        ]
       }
      },
@@ -210268,7 +210284,7 @@
     ]
    },
    "lint.ignore": [
-    "acdeddbff3935e919bc582bdbdb1af1d6693f26d",
+    "49f88ddc7b339434e071522958fcc2a6aec5d237",
     []
    ],
    "loading": {
@@ -220850,7 +220866,7 @@
       ],
       "tasks": {
        "test.yml": [
-        "6e22cc57081ad222c5fd6df7f3783da8ed44d72d",
+        "318437a80ec3240721a17cc6253f241109038b64",
         []
        ]
       },
@@ -226257,8 +226273,12 @@
         "bf829d93e968d4a6bccfa6798fb974bf98ff7d60",
         []
        ],
+       "actions.py": [
+        "fc43dd665a2f3bc5576cb535f29d49abbc9593fd",
+        []
+       ],
        "base.py": [
-        "ab36c95c11339951c9c5ccd5da4c19551ba8f8de",
+        "5d6c283737e6a768279f69503094345c0f942880",
         []
        ],
        "executorchrome.py": [
@@ -277248,7 +277268,7 @@
        ]
       ],
       "outline-width-interpolation.html": [
-       "c024c7cf6a08e0f6e02ccb451ca04d0b4a8c9251",
+       "a46907a169074b9af0ac85d7f39ebc0e568f8c64",
        [
         null,
         {}
@@ -277523,7 +277543,7 @@
        ]
       ],
       "outline-width-computed.html": [
-       "33eb9c9b99cba713d282010e4ddd5293b7b00aac",
+       "abd5826bbb387ec6dcbebfcd9e2870df8aea6ab9",
        [
         null,
         {}
@@ -323997,7 +324017,7 @@
       ]
      ],
      "cache-storage-reporting-dedicated-worker.https.html": [
-      "c9c2a9ac715658dc8c7a1d5f66bf0e8d93912aa4",
+      "d88bf56ec2cb16dc3ab03035a0bdf15f10f54c04",
       [
        null,
        {
@@ -324006,7 +324026,7 @@
       ]
      ],
      "cache-storage-reporting-document.https.html": [
-      "d5ae757facfc5742a1bbb134dbd67bd6fbdeb4df",
+      "efb7e7a779587de7f5c142383d36470a54908f5c",
       [
        null,
        {
@@ -324015,16 +324035,14 @@
       ]
      ],
      "cache-storage-reporting-service-worker.https.html": [
-      "6b5b78bba088671d4d6eb5b7833bf1c873963eee",
+      "b9ec8d95a59ade8a8120a0af04248d3c930c1dac",
       [
        null,
-       {
-        "timeout": "long"
-       }
+       {}
       ]
      ],
      "cache-storage-reporting-shared-worker.https.html": [
-      "0b13de8657f93aeba0980831f98e378b753f4c83",
+      "d7bd191a84e9c2e5b766ea258ac615791471fb95",
       [
        null,
        {
@@ -324134,6 +324152,15 @@
        }
       ]
      ],
+     "reporting-subresource-corp.https.html": [
+      "aa76bbe8d21d259ed0b9dcadb1d942635134ac79",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
      "reporting.https.html": [
       "42d5e6cc33537e988daf77d740fdcdeab64508c5",
       [
@@ -330909,6 +330936,13 @@
          {}
         ]
        ],
+       "iframe-loading-lazy-referrerpolicy-change.sub.tentative.html": [
+        "145df404003cdb3ee1c0688351a5300603780789",
+        [
+         null,
+         {}
+        ]
+       ],
        "iframe-nosrc.html": [
         "57189a0b884d3a55e4bb2ba1a1d3aa83066c0eb3",
         [
@@ -331565,7 +331599,7 @@
         ]
        ],
        "image-loading-lazy-crossorigin-change.sub.html": [
-        "13560324854dceeca26bda3433301cafe07fb62a",
+        "3fb827df2d6159de73c9fe0417cf790d0d484ff6",
         [
          null,
          {}
@@ -331634,6 +331668,13 @@
          {}
         ]
        ],
+       "image-loading-lazy-referrerpolicy-change.sub.html": [
+        "307d9571b498e108f121b97f02dc56412cd6f21e",
+        [
+         null,
+         {}
+        ]
+       ],
        "image-loading-lazy-srcset.html": [
         "21c11c7233c7b8f845a31b6aa25dda14ca381960",
         [
@@ -331753,13 +331794,6 @@
          {}
         ]
        ],
-       "original-referrer-policy-applied.sub.html": [
-        "c300119f72fbd3742d6dbf7e2439d967911303a5",
-        [
-         null,
-         {}
-        ]
-       ],
        "picture-loading-lazy.html": [
         "0ab2fb27367309814094071ccad7fa2922aa80f9",
         [
@@ -336061,7 +336095,7 @@
          ]
         ],
         "referrer-origin-when-cross-origin.sub.html": [
-         "4cd427ee88766f7f836ef86a3617244864ac5e40",
+         "fd4c1ea49649cf348d99ab0d881aa8f966be416e",
          [
           null,
           {}
@@ -364511,6 +364545,13 @@
       {}
      ]
     ],
+    "finish-animation.html": [
+     "87bcc41a6a222d883ebe2b249ae103b05275019d",
+     [
+      null,
+      {}
+     ]
+    ],
     "idlharness.window.js": [
      "90157580ce00716403346f369b1e25bba8db23c2",
      [
@@ -406753,7 +406794,7 @@
       ]
      ],
      "ruby-reflow-001-noruby.html": [
-      "4c00573422b3954853965a3d86ad1464a53a244f",
+      "eda07deddb2d5ec93f45675854998f2b27d5a683",
       [
        null,
        {}
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coop-sandbox.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coop-sandbox.https.html
index 5fd1164..6f10945c 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coop-sandbox.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coop-sandbox.https.html
@@ -26,4 +26,20 @@
     }));
   }, `<iframe sandbox="${sandboxValue}"> ${document.title}`);
 });
+
+async_test(t => {
+  const frame = document.createElement("iframe");
+  const channel = new BroadcastChannel(token());
+  frame.sandbox = "allow-scripts allow-same-origin";
+  frame.name = `iframe-${channel.name}`;
+  frame.src = `resources/coop-coep.py?coop=same-origin&coep=&channel=${channel.name}`;
+  channel.onmessage = t.step_func( event => {
+    const payload = event.data;
+    assert_equals(payload.name, frame.name, "name");
+    t.done();
+  });
+  t.step_timeout(t.unreached_func("Timed out while waiting for iframe's message"), 1500);
+  t.add_cleanup(() => frame.remove());
+  document.body.append(frame);
+}, `Iframe with sandbox and COOP must load.`);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/referrer-origin-when-cross-origin.sub.html b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/referrer-origin-when-cross-origin.sub.html
index 4cd427e..fd4c1ea4 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/referrer-origin-when-cross-origin.sub.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/module/referrer-origin-when-cross-origin.sub.html
@@ -24,7 +24,7 @@
 import { referrer as referrerRemoteSame } from "http://{{domains[www1]}}:{{ports[http][0]}}/html/semantics/scripting-1/the-script-element/module/resources/import-same-origin-referrer-checker-from-remote-origin.sub.js?name=remote_same";
 
 const origin = (new URL(location.href)).origin + "/";
-const remoteOrigin = "http://{{domains[www1]}}:{{ports[http][0]}}/";
+const remoteOrigin = new URL("http://{{domains[www1]}}:{{ports[http][0]}}/").origin + "/";
 
 test(t => {
   assert_equals(
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/tc/tasks/test.yml b/third_party/blink/web_tests/external/wpt/tools/ci/tc/tasks/test.yml
index 6e22cc5..318437a 100644
--- a/third_party/blink/web_tests/external/wpt/tools/ci/tc/tasks/test.yml
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/tc/tasks/test.yml
@@ -230,6 +230,7 @@
               --
               --channel=${vars.channel}
               --verify
+              --verify-no-chaos-mode
 
         - wpt-${vars.browser}-${vars.channel}-results:
             use:
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/actions.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/actions.py
new file mode 100644
index 0000000..fc43dd6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/actions.py
@@ -0,0 +1,184 @@
+
+class ClickAction(object):
+    name = "click"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        selector = payload["selector"]
+        element = self.protocol.select.element_by_selector(selector)
+        self.logger.debug("Clicking element: %s" % selector)
+        self.protocol.click.element(element)
+
+
+class SendKeysAction(object):
+    name = "send_keys"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        selector = payload["selector"]
+        keys = payload["keys"]
+        element = self.protocol.select.element_by_selector(selector)
+        self.logger.debug("Sending keys to element: %s" % selector)
+        self.protocol.send_keys.send_keys(element, keys)
+
+
+class ActionSequenceAction(object):
+    name = "action_sequence"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        # TODO: some sort of shallow error checking
+        actions = payload["actions"]
+        for actionSequence in actions:
+            if actionSequence["type"] == "pointer":
+                for action in actionSequence["actions"]:
+                    if (action["type"] == "pointerMove" and
+                        isinstance(action["origin"], dict)):
+                        action["origin"] = self.get_element(action["origin"]["selector"], action["frame"]["frame"])
+        self.protocol.action_sequence.send_actions({"actions": actions})
+
+    def get_element(self, element_selector, frame):
+        element = self.protocol.select.element_by_selector(element_selector, frame)
+        return element
+
+class GenerateTestReportAction(object):
+    name = "generate_test_report"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        message = payload["message"]
+        self.logger.debug("Generating test report: %s" % message)
+        self.protocol.generate_test_report.generate_test_report(message)
+
+class SetPermissionAction(object):
+    name = "set_permission"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        permission_params = payload["permission_params"]
+        descriptor = permission_params["descriptor"]
+        name = descriptor["name"]
+        state = permission_params["state"]
+        one_realm = permission_params.get("oneRealm", False)
+        self.logger.debug("Setting permission %s to %s, oneRealm=%s" % (name, state, one_realm))
+        self.protocol.set_permission.set_permission(descriptor, state, one_realm)
+
+class AddVirtualAuthenticatorAction(object):
+    name = "add_virtual_authenticator"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        self.logger.debug("Adding virtual authenticator")
+        config = payload["config"]
+        authenticator_id = self.protocol.virtual_authenticator.add_virtual_authenticator(config)
+        self.logger.debug("Authenticator created with ID %s" % authenticator_id)
+        return authenticator_id
+
+class RemoveVirtualAuthenticatorAction(object):
+    name = "remove_virtual_authenticator"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        authenticator_id = payload["authenticator_id"]
+        self.logger.debug("Removing virtual authenticator %s" % authenticator_id)
+        return self.protocol.virtual_authenticator.remove_virtual_authenticator(authenticator_id)
+
+
+class AddCredentialAction(object):
+    name = "add_credential"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        authenticator_id = payload["authenticator_id"]
+        credential = payload["credential"]
+        self.logger.debug("Adding credential to virtual authenticator %s " % authenticator_id)
+        return self.protocol.virtual_authenticator.add_credential(authenticator_id, credential)
+
+class GetCredentialsAction(object):
+    name = "get_credentials"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        authenticator_id = payload["authenticator_id"]
+        self.logger.debug("Getting credentials from virtual authenticator %s " % authenticator_id)
+        return self.protocol.virtual_authenticator.get_credentials(authenticator_id)
+
+class RemoveCredentialAction(object):
+    name = "remove_credential"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        authenticator_id = payload["authenticator_id"]
+        credential_id = payload["credential_id"]
+        self.logger.debug("Removing credential %s from authenticator %s" % (credential_id, authenticator_id))
+        return self.protocol.virtual_authenticator.remove_credential(authenticator_id, credential_id)
+
+class RemoveAllCredentialsAction(object):
+    name = "remove_all_credentials"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        authenticator_id = payload["authenticator_id"]
+        self.logger.debug("Removing all credentials from authenticator %s" % authenticator_id)
+        return self.protocol.virtual_authenticator.remove_all_credentials(authenticator_id)
+
+class SetUserVerifiedAction(object):
+    name = "set_user_verified"
+
+    def __init__(self, logger, protocol):
+        self.logger = logger
+        self.protocol = protocol
+
+    def __call__(self, payload):
+        authenticator_id = payload["authenticator_id"]
+        uv = payload["uv"]
+        self.logger.debug(
+            "Setting user verified flag on authenticator %s to %s" % (authenticator_id, uv["isUserVerified"]))
+        return self.protocol.virtual_authenticator.set_user_verified(authenticator_id, uv)
+
+
+actions = [ClickAction,
+           SendKeysAction,
+           ActionSequenceAction,
+           GenerateTestReportAction,
+           SetPermissionAction,
+           AddVirtualAuthenticatorAction,
+           RemoveVirtualAuthenticatorAction,
+           AddCredentialAction,
+           GetCredentialsAction,
+           RemoveCredentialAction,
+           RemoveAllCredentialsAction,
+           SetUserVerifiedAction]
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/base.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/base.py
index ab36c95..5d6c2837 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/base.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/base.py
@@ -13,6 +13,7 @@
 from six.moves.urllib.parse import urljoin, urlsplit, urlunsplit
 
 from ..testrunner import Stop
+from .actions import actions
 from .protocol import Protocol, BaseProtocolPart
 
 here = os.path.split(__file__)[0]
@@ -681,20 +682,7 @@
             "complete": self.process_complete
         }
 
-        self.actions = {
-            "click": ClickAction(self.logger, self.protocol),
-            "send_keys": SendKeysAction(self.logger, self.protocol),
-            "action_sequence": ActionSequenceAction(self.logger, self.protocol),
-            "generate_test_report": GenerateTestReportAction(self.logger, self.protocol),
-            "set_permission": SetPermissionAction(self.logger, self.protocol),
-            "add_virtual_authenticator": AddVirtualAuthenticatorAction(self.logger, self.protocol),
-            "remove_virtual_authenticator": RemoveVirtualAuthenticatorAction(self.logger, self.protocol),
-            "add_credential": AddCredentialAction(self.logger, self.protocol),
-            "get_credentials": GetCredentialsAction(self.logger, self.protocol),
-            "remove_credential": RemoveCredentialAction(self.logger, self.protocol),
-            "remove_all_credentials": RemoveAllCredentialsAction(self.logger, self.protocol),
-            "set_user_verified": SetUserVerifiedAction(self.logger, self.protocol),
-        }
+        self.actions = {cls.name: cls(self.logger, self.protocol) for cls in actions}
 
     def __call__(self, result):
         url, command, payload = result
@@ -735,150 +723,3 @@
 
     def _send_message(self, message_type, status, message=None):
         self.protocol.testdriver.send_message(message_type, status, message=message)
-
-
-class ClickAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        selector = payload["selector"]
-        element = self.protocol.select.element_by_selector(selector)
-        self.logger.debug("Clicking element: %s" % selector)
-        self.protocol.click.element(element)
-
-
-class SendKeysAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        selector = payload["selector"]
-        keys = payload["keys"]
-        element = self.protocol.select.element_by_selector(selector)
-        self.logger.debug("Sending keys to element: %s" % selector)
-        self.protocol.send_keys.send_keys(element, keys)
-
-
-class ActionSequenceAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        # TODO: some sort of shallow error checking
-        actions = payload["actions"]
-        for actionSequence in actions:
-            if actionSequence["type"] == "pointer":
-                for action in actionSequence["actions"]:
-                    if (action["type"] == "pointerMove" and
-                        isinstance(action["origin"], dict)):
-                        action["origin"] = self.get_element(action["origin"]["selector"], action["frame"]["frame"])
-        self.protocol.action_sequence.send_actions({"actions": actions})
-
-    def get_element(self, element_selector, frame):
-        element = self.protocol.select.element_by_selector(element_selector, frame)
-        return element
-
-class GenerateTestReportAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        message = payload["message"]
-        self.logger.debug("Generating test report: %s" % message)
-        self.protocol.generate_test_report.generate_test_report(message)
-
-class SetPermissionAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        permission_params = payload["permission_params"]
-        descriptor = permission_params["descriptor"]
-        name = descriptor["name"]
-        state = permission_params["state"]
-        one_realm = permission_params.get("oneRealm", False)
-        self.logger.debug("Setting permission %s to %s, oneRealm=%s" % (name, state, one_realm))
-        self.protocol.set_permission.set_permission(descriptor, state, one_realm)
-
-class AddVirtualAuthenticatorAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        self.logger.debug("Adding virtual authenticator")
-        config = payload["config"]
-        authenticator_id = self.protocol.virtual_authenticator.add_virtual_authenticator(config)
-        self.logger.debug("Authenticator created with ID %s" % authenticator_id)
-        return authenticator_id
-
-class RemoveVirtualAuthenticatorAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        authenticator_id = payload["authenticator_id"]
-        self.logger.debug("Removing virtual authenticator %s" % authenticator_id)
-        return self.protocol.virtual_authenticator.remove_virtual_authenticator(authenticator_id)
-
-
-class AddCredentialAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        authenticator_id = payload["authenticator_id"]
-        credential = payload["credential"]
-        self.logger.debug("Adding credential to virtual authenticator %s " % authenticator_id)
-        return self.protocol.virtual_authenticator.add_credential(authenticator_id, credential)
-
-class GetCredentialsAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        authenticator_id = payload["authenticator_id"]
-        self.logger.debug("Getting credentials from virtual authenticator %s " % authenticator_id)
-        return self.protocol.virtual_authenticator.get_credentials(authenticator_id)
-
-class RemoveCredentialAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        authenticator_id = payload["authenticator_id"]
-        credential_id = payload["credential_id"]
-        self.logger.debug("Removing credential %s from authenticator %s" % (credential_id, authenticator_id))
-        return self.protocol.virtual_authenticator.remove_credential(authenticator_id, credential_id)
-
-class RemoveAllCredentialsAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        authenticator_id = payload["authenticator_id"]
-        self.logger.debug("Removing all credentials from authenticator %s" % authenticator_id)
-        return self.protocol.virtual_authenticator.remove_all_credentials(authenticator_id)
-
-class SetUserVerifiedAction(object):
-    def __init__(self, logger, protocol):
-        self.logger = logger
-        self.protocol = protocol
-
-    def __call__(self, payload):
-        authenticator_id = payload["authenticator_id"]
-        uv = payload["uv"]
-        self.logger.debug(
-            "Setting user verified flag on authenticator %s to %s" % (authenticator_id, uv["isUserVerified"]))
-        return self.protocol.virtual_authenticator.set_user_verified(authenticator_id, uv)
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/function-generator-details-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/function-generator-details-expected.txt
index 5c8f1d4..899313af 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/function-generator-details-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/function-generator-details-expected.txt
@@ -1,7 +1,7 @@
 Tests that Debugger.getGeneratorObjectDetails command returns correct result.
 
 
-Running: testIterNotStarted
+Running: testiterNotStarted
 iterNotStarted: type = object, subtype = generator
 lineNumber = 17
 columnNumber = 19
@@ -16,7 +16,7 @@
 [[GeneratorReceiver]] = Window
 [[Scopes]] = Scopes[2]
 
-Running: testIterSuspended
+Running: testiterSuspended
 iterSuspended: type = object, subtype = generator
 lineNumber = 19
 columnNumber = 10
@@ -31,7 +31,7 @@
 [[GeneratorReceiver]] = Window
 [[Scopes]] = Scopes[2]
 
-Running: testIterClosed
+Running: testiterClosed
 iterClosed: type = object, subtype = generator
 lineNumber = 17
 columnNumber = 19
@@ -45,7 +45,7 @@
       }
 [[GeneratorReceiver]] = Window
 
-Running: testIterObjGenerator
+Running: testiterObjGenerator
 iterObjGenerator: type = object, subtype = generator
 lineNumber = 28
 columnNumber = 14
@@ -60,7 +60,7 @@
 [[GeneratorReceiver]] = Object
 [[Scopes]] = Scopes[2]
 
-Running: testAnonymousGenIter
+Running: testanonymousGenIter
 anonymousGenIter: type = object, subtype = generator
 lineNumber = 42
 columnNumber = 10
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/function-generator-details.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/function-generator-details.js
index 4d40b13b..49aa1e454 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/function-generator-details.js
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/function-generator-details.js
@@ -86,7 +86,7 @@
   ];
 
   function createTestSuiteFunction(expression) {
-    var functionName = 'test' + expression.toTitleCase();
+    var functionName = 'test' + expression;
     return eval(
         'function ' + functionName + '(next)\n' +
         '{\n' +
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/show-generator-location-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/show-generator-location-expected.txt
index 0a293f9..1be1543 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/show-generator-location-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/show-generator-location-expected.txt
@@ -1,18 +1,18 @@
 Tests that "Show Generator Location" jumps to the correct location.
 
 
-Running: testIterNotStarted
+Running: testiterNotStarted
 Generator location revealed: [17:20]
 
-Running: testIterSuspended1
+Running: testiterSuspended1
 Generator location revealed: [19:11]
 
-Running: testIterSuspended2
+Running: testiterSuspended2
 Generator location revealed: [20:11]
 
-Running: testIterSuspended3
+Running: testiterSuspended3
 Generator location revealed: [21:11]
 
-Running: testIterClosed
+Running: testiterClosed
 Generator location revealed: [17:20]
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/show-generator-location.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/show-generator-location.js
index 771124d2..543b79e 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/show-generator-location.js
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/show-generator-location.js
@@ -66,7 +66,7 @@
   ];
 
   function createTestSuiteFunction(expression) {
-    var functionName = 'test' + expression.toTitleCase();
+    var functionName = 'test' + expression;
     return eval(
         'function ' + functionName + '(next)\n' +
         '{\n' +
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/quic-transport-handshake-failure-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/quic-transport-handshake-failure-expected.txt
new file mode 100644
index 0000000..f1ae8e5
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/quic-transport-handshake-failure-expected.txt
@@ -0,0 +1,8 @@
+Check the console message printed on a QuicTransport handshake failure.
+Log Enabled
+Instantiate QuicTransport.
+Log.onEntryAdded
+source: network
+level: error
+text: Failed to establish a connection to quic-transport://localhost/.
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/quic-transport-handshake-failure.js b/third_party/blink/web_tests/http/tests/inspector-protocol/quic-transport-handshake-failure.js
new file mode 100644
index 0000000..5047742
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/quic-transport-handshake-failure.js
@@ -0,0 +1,20 @@
+(async function(testRunner) {
+  const {session, dp} = await testRunner.startBlank(
+      `Check the console message printed on a QuicTransport handshake failure.`);
+  const url = 'quic-transport://localhost';
+
+  await dp.Log.enable();
+  testRunner.log('Log Enabled');
+
+  dp.Log.onEntryAdded(event => {
+    const entry = event.params.entry;
+    testRunner.log('Log.onEntryAdded');
+    testRunner.log(`source: ${entry.source}`);
+    testRunner.log(`level: ${entry.level}`);
+    testRunner.log(`text: ${entry.text}`);
+    testRunner.completeTest();
+  });
+
+  session.evaluate(`new QuicTransport('${url}');`);
+  testRunner.log('Instantiate QuicTransport.');
+})
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/contacts-manager-extra-properties-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/contacts-manager-extra-properties-trial-interfaces.html
deleted file mode 100644
index c3345589..0000000
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/contacts-manager-extra-properties-trial-interfaces.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<!-- Generate token with the command:
-generate_token.py http://127.0.0.1:8000 ContactsManagerExtraProperties --expire-timestamp=2000000000
--->
-<meta http-equiv="origin-trial" content="Ag08kQBxaZIHRn4szFwJFlEfUhOPCV2CZtU7mqRMQqxt3kGuuPu9Qom6hRdF+vAQIBcHOrDw0d4ajhMzeUETXQ0AAABmeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiQ29udGFjdHNNYW5hZ2VyRXh0cmFQcm9wZXJ0aWVzIiwgImV4cGlyeSI6IDIwMDAwMDAwMDB9" />
-<title>Contacts Manager Extra Properties - exposed by origin trial</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/origin-trials-helper.js"></script>
-<script>
-
-test(() => {
-  OriginTrialsHelper.check_properties_exist(this, { 'ContactsManager': ['select', 'getProperties'] });
-}, 'Contact API interfaces and properties in Origin-Trial enabled document.');
-
-promise_test(async () => {
-  const properties = await navigator.contacts.getProperties();
-  assert_true(properties.includes('address'));
-  assert_true(properties.includes('icon'));
-}, '`address` and `icon` properties are exposed.');
-
-</script>
diff --git a/third_party/blink/web_tests/inspector-protocol/debugger/wasm-streaming-url-expected.txt b/third_party/blink/web_tests/inspector-protocol/debugger/wasm-streaming-url-expected.txt
index dcab134..11cedcb 100644
--- a/third_party/blink/web_tests/inspector-protocol/debugger/wasm-streaming-url-expected.txt
+++ b/third_party/blink/web_tests/inspector-protocol/debugger/wasm-streaming-url-expected.txt
@@ -1,6 +1,6 @@
 Test script URL for wasm streaming compilation.
 Did enable debugger
-Instantiating.
-Waiting for wasm script
-URL is preserved: true
+URL is preserved in the main thread: true
+Worker created
+URL is preserved in the worker: true
 
diff --git a/third_party/blink/web_tests/inspector-protocol/debugger/wasm-streaming-url.js b/third_party/blink/web_tests/inspector-protocol/debugger/wasm-streaming-url.js
index 544982b..497ac76 100644
--- a/third_party/blink/web_tests/inspector-protocol/debugger/wasm-streaming-url.js
+++ b/third_party/blink/web_tests/inspector-protocol/debugger/wasm-streaming-url.js
@@ -2,14 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-function instantiate() {
+function compileStreamingAndSendToWorker() {
+  let workerBlob = new Blob([`
+  onmessage = function(e) {
+    let instance = new WebAssembly.Instance(e.data.module);
+  }`],
+  {type: 'application/javascript'});
+  let worker = new Worker(URL.createObjectURL(workerBlob));
+
   const magicModuleHeader = [0x00, 0x61, 0x73, 0x6d], moduleVersion = [0x01, 0x00, 0x00, 0x00];
   const wasm = Uint8Array.from([...magicModuleHeader, ...moduleVersion]);
   let b = new Blob([wasm.buffer], {type: 'application/wasm'});
   let bURL =  URL.createObjectURL(b);
+
   fetch(bURL)
-  .then(WebAssembly.instantiateStreaming)
-  .catch(console.error);
+  .then(WebAssembly.compileStreaming)
+  .then(module => worker.postMessage(module))
   return bURL.toString();
 }
 
@@ -19,13 +27,23 @@
 
   await dp.Debugger.enable();
   testRunner.log('Did enable debugger');
+  let url = await session.evaluate('(' + compileStreamingAndSendToWorker + ')()');
 
-  testRunner.log('Instantiating.');
-  var response = await dp.Runtime
-      .evaluate({expression: '(' + instantiate + ')()'});
-  testRunner.log('Waiting for wasm script');
-  const {params: wasm_script} = await dp.Debugger.onceScriptParsed();
+  // Wait for script creation in the main thread.
+  let {params: main_script} = await dp.Debugger.onceScriptParsed();
+  testRunner.log('URL is preserved in the main thread: ' + (main_script.url == url));
 
-  testRunner.log('URL is preserved: ' + (wasm_script.url == response.result.result.value));
+  // Wait for worker.
+  const attachedPromise = dp.Target.onceAttachedToTarget();
+  await dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false,
+                           flatten: true});
+  const event = await attachedPromise;
+  const childSession = session.createChild(event.params.sessionId);
+  testRunner.log('Worker created');
+
+  // Wait for script creation in the worker.
+  await childSession.protocol.Debugger.enable({});
+  let {params: worker_script} = await childSession.protocol.Debugger.onceScriptParsed();
+  testRunner.log('URL is preserved in the worker: ' + (worker_script.url == url));
   testRunner.completeTest();
 })
diff --git a/third_party/blink/web_tests/platform/linux/editing/selection/caret-ltr-2-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/caret-ltr-2-expected.png
deleted file mode 100644
index 14bcc3c..0000000
--- a/third_party/blink/web_tests/platform/linux/editing/selection/caret-ltr-2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/selection/caret-ltr-2-left-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/caret-ltr-2-left-expected.png
deleted file mode 100644
index 9fc79ef3..0000000
--- a/third_party/blink/web_tests/platform/linux/editing/selection/caret-ltr-2-left-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-2-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-2-expected.png
deleted file mode 100644
index 635fe4f..0000000
--- a/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-2-left-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-2-left-expected.png
deleted file mode 100644
index 5b84dbf..0000000
--- a/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-2-left-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-expected.png
deleted file mode 100644
index 67c9037..0000000
--- a/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-right-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-right-expected.png
deleted file mode 100644
index 1d62fbf7..0000000
--- a/third_party/blink/web_tests/platform/linux/editing/selection/caret-rtl-right-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/selection/caret-ltr-2-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/caret-ltr-2-expected.png
deleted file mode 100644
index 83c402bc..0000000
--- a/third_party/blink/web_tests/platform/mac/editing/selection/caret-ltr-2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/selection/caret-ltr-2-left-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/caret-ltr-2-left-expected.png
deleted file mode 100644
index abe1f25be..0000000
--- a/third_party/blink/web_tests/platform/mac/editing/selection/caret-ltr-2-left-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-2-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-2-expected.png
deleted file mode 100644
index 9c9a6a3..0000000
--- a/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-2-left-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-2-left-expected.png
deleted file mode 100644
index 3ef829c..0000000
--- a/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-2-left-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-expected.png
deleted file mode 100644
index e7634af..0000000
--- a/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-right-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-right-expected.png
deleted file mode 100644
index 494efca..0000000
--- a/third_party/blink/web_tests/platform/mac/editing/selection/caret-rtl-right-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/selection/caret-ltr-2-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/caret-ltr-2-expected.png
deleted file mode 100644
index b7003f3..0000000
--- a/third_party/blink/web_tests/platform/win/editing/selection/caret-ltr-2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/selection/caret-ltr-2-left-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/caret-ltr-2-left-expected.png
deleted file mode 100644
index 98b1381c..0000000
--- a/third_party/blink/web_tests/platform/win/editing/selection/caret-ltr-2-left-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-2-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-2-expected.png
deleted file mode 100644
index 7a22f25f..0000000
--- a/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-2-left-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-2-left-expected.png
deleted file mode 100644
index 98eb2f1ad..0000000
--- a/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-2-left-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-expected.png
deleted file mode 100644
index ec96dec6..0000000
--- a/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-right-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-right-expected.png
deleted file mode 100644
index 3444f8d..0000000
--- a/third_party/blink/web_tests/platform/win/editing/selection/caret-rtl-right-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt
index c2ace28..c5c521b 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 527 tests; 334 PASS, 193 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 527 tests; 337 PASS, 190 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -410,7 +410,7 @@
 PASS Parsing: <..> against <file:///C:/>
 PASS Parsing: <..> against <file:///>
 PASS Parsing: </> against <file:///C:/a/b>
-FAIL Parsing: <//d:> against <file:///C:/a/b> assert_equals: href expected "file:///d:" but got "file:///D:/"
+FAIL Parsing: <//d:> against <file:///C:/a/b> assert_equals: href expected "file:///d:" but got "file:///D:"
 FAIL Parsing: <//d:/..> against <file:///C:/a/b> assert_equals: href expected "file:///d:/" but got "file:///D:/"
 PASS Parsing: <..> against <file:///ab:/>
 PASS Parsing: <..> against <file:///1:/>
@@ -437,9 +437,9 @@
 PASS Parsing: <file://> against <file://ape/>
 PASS Parsing: </rooibos> against <file://tea/>
 PASS Parsing: </?chai> against <file://tea/>
-FAIL Parsing: <C|> against <file://host/dir/file> assert_equals: href expected "file:///C:" but got "file:///C:/"
-FAIL Parsing: <C|#> against <file://host/dir/file> assert_equals: href expected "file:///C:#" but got "file:///C:/#"
-FAIL Parsing: <C|?> against <file://host/dir/file> assert_equals: href expected "file:///C:?" but got "file:///C:/?"
+PASS Parsing: <C|> against <file://host/dir/file>
+PASS Parsing: <C|#> against <file://host/dir/file>
+PASS Parsing: <C|?> against <file://host/dir/file>
 PASS Parsing: <C|/> against <file://host/dir/file>
 PASS Parsing: <C|
 /> against <file://host/dir/file>
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
index c2ace28..c5c521b 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 527 tests; 334 PASS, 193 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 527 tests; 337 PASS, 190 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -410,7 +410,7 @@
 PASS Parsing: <..> against <file:///C:/>
 PASS Parsing: <..> against <file:///>
 PASS Parsing: </> against <file:///C:/a/b>
-FAIL Parsing: <//d:> against <file:///C:/a/b> assert_equals: href expected "file:///d:" but got "file:///D:/"
+FAIL Parsing: <//d:> against <file:///C:/a/b> assert_equals: href expected "file:///d:" but got "file:///D:"
 FAIL Parsing: <//d:/..> against <file:///C:/a/b> assert_equals: href expected "file:///d:/" but got "file:///D:/"
 PASS Parsing: <..> against <file:///ab:/>
 PASS Parsing: <..> against <file:///1:/>
@@ -437,9 +437,9 @@
 PASS Parsing: <file://> against <file://ape/>
 PASS Parsing: </rooibos> against <file://tea/>
 PASS Parsing: </?chai> against <file://tea/>
-FAIL Parsing: <C|> against <file://host/dir/file> assert_equals: href expected "file:///C:" but got "file:///C:/"
-FAIL Parsing: <C|#> against <file://host/dir/file> assert_equals: href expected "file:///C:#" but got "file:///C:/#"
-FAIL Parsing: <C|?> against <file://host/dir/file> assert_equals: href expected "file:///C:?" but got "file:///C:/?"
+PASS Parsing: <C|> against <file://host/dir/file>
+PASS Parsing: <C|#> against <file://host/dir/file>
+PASS Parsing: <C|?> against <file://host/dir/file>
 PASS Parsing: <C|/> against <file://host/dir/file>
 PASS Parsing: <C|
 /> against <file://host/dir/file>
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor-expected.txt
index 9c6ddd5..7934a6b 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 527 tests; 399 PASS, 128 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 527 tests; 402 PASS, 125 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -450,7 +450,7 @@
 PASS Parsing: <..> against <file:///C:/>
 PASS Parsing: <..> against <file:///>
 PASS Parsing: </> against <file:///C:/a/b>
-FAIL Parsing: <//d:> against <file:///C:/a/b> assert_equals: href expected "file:///d:" but got "file:///D:/"
+FAIL Parsing: <//d:> against <file:///C:/a/b> assert_equals: href expected "file:///d:" but got "file:///D:"
 FAIL Parsing: <//d:/..> against <file:///C:/a/b> assert_equals: href expected "file:///d:/" but got "file:///D:/"
 PASS Parsing: <..> against <file:///ab:/>
 PASS Parsing: <..> against <file:///1:/>
@@ -477,9 +477,9 @@
 PASS Parsing: <file://> against <file://ape/>
 PASS Parsing: </rooibos> against <file://tea/>
 PASS Parsing: </?chai> against <file://tea/>
-FAIL Parsing: <C|> against <file://host/dir/file> assert_equals: href expected "file:///C:" but got "file:///C:/"
-FAIL Parsing: <C|#> against <file://host/dir/file> assert_equals: href expected "file:///C:#" but got "file:///C:/#"
-FAIL Parsing: <C|?> against <file://host/dir/file> assert_equals: href expected "file:///C:?" but got "file:///C:/?"
+PASS Parsing: <C|> against <file://host/dir/file>
+PASS Parsing: <C|#> against <file://host/dir/file>
+PASS Parsing: <C|?> against <file://host/dir/file>
 PASS Parsing: <C|/> against <file://host/dir/file>
 PASS Parsing: <C|
 /> against <file://host/dir/file>
diff --git a/third_party/blink/web_tests/platform/win/fast/url/relative-win-expected.txt b/third_party/blink/web_tests/platform/win/fast/url/relative-win-expected.txt
index 54c11a5..f84c2ee 100644
--- a/third_party/blink/web_tests/platform/win/fast/url/relative-win-expected.txt
+++ b/third_party/blink/web_tests/platform/win/fast/url/relative-win-expected.txt
@@ -9,7 +9,7 @@
 FAIL canonicalize('\\\\another\\path') should be . Was file://another/path.
 PASS canonicalize('//c:/foo') is 'file:///C:/foo'
 PASS canonicalize('//localhost/c:/foo') is 'file:///C:/foo'
-FAIL canonicalize('c:') should be . Was file:///C:/.
+FAIL canonicalize('c:') should be . Was file:///C:.
 FAIL canonicalize('c:/foo') should be . Was file:///C:/foo.
 FAIL canonicalize('c:\\foo') should be . Was file:///C:/foo.
 PASS canonicalize('/z:/bar') is 'file:///Z:/bar'
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event.html
index 0e02af2..c3a3605 100644
--- a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event.html
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/date-multiple-fields/date-multiple-fields-wheel-event.html
@@ -2,6 +2,7 @@
 <html>
 <head>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../../../resources/gesture-util.js"></script>
 <script src="../resources/common-wheel-event.js"></script>
 </head>
 <body>
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event.html
index d8e3c05..c7db2c1f 100644
--- a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event.html
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-wheel-event.html
@@ -2,6 +2,7 @@
 <html>
 <head>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../../../resources/gesture-util.js"></script>
 <script src="../resources/common-wheel-event.js"></script>
 </head>
 <body>
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event.html
index a1d3a22..bbfeaec 100644
--- a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event.html
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/month-multiple-fields/month-multiple-fields-wheel-event.html
@@ -2,6 +2,7 @@
 <html>
 <head>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../../../resources/gesture-util.js"></script>
 <script src="../resources/common-wheel-event.js"></script>
 </head>
 <body>
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-wheel-event.js b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-wheel-event.js
index 8e952e6..58299e5 100644
--- a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-wheel-event.js
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/resources/common-wheel-event.js
@@ -1,13 +1,22 @@
-function dispatchWheelEvent(element, deltaX, deltaY)
+async function dispatchWheelEvent(element, deltaX, deltaY)
 {
     var rect = element.getClientRects()[0]
-    eventSender.mouseMoveTo(rect.left, rect.top);
-    eventSender.mouseScrollBy(deltaX, deltaY);
+
+    const x = rect.left;
+    const y = rect.top;
+    const source = GestureSourceType.MOUSE_INPUT;
+    const speed = SPEED_INSTANT;
+    const precise_scrolling_deltas = false;
+
+    await mouseMoveTo(x, y);
+    await smoothScrollWithXY(deltaX, deltaY, x, y, source, speed,
+        precise_scrolling_deltas);
 }
 
 var input;
-function testWheelEvent(parameters)
+async function testWheelEvent(parameters)
 {
+    window.jsTestIsAsync = true;
     var inputType = parameters['inputType'];
     var initialValue = parameters['initialValue'];
     var stepUpValue1 = parameters['stepUpValue1'];
@@ -20,38 +29,39 @@
     input.focus();
 
     debug('Initial value is ' + initialValue + '. We\'ll wheel up by 1:');
-    dispatchWheelEvent(input, 0, 1);
+    await dispatchWheelEvent(input, 0, -1);
     shouldBeEqualToString('input.value', stepUpValue1);
 
     debug('Wheel up by 100:');
-    dispatchWheelEvent(input, 0, 100);
+    await dispatchWheelEvent(input, 0, -100);
     shouldBeEqualToString('input.value', stepUpValue2);
 
     debug('Wheel down by 1:');
-    dispatchWheelEvent(input, 0, -1);
+    await dispatchWheelEvent(input, 0, 1);
     shouldBeEqualToString('input.value', stepUpValue1);
 
     debug('Wheel down by 256:');
-    dispatchWheelEvent(input, 0, -256);
+    await dispatchWheelEvent(input, 0, 256);
     shouldBeEqualToString('input.value', initialValue);
 
     debug('Disabled input element:');
     input.disabled = true;
-    dispatchWheelEvent(input, 0, 1);
+    await dispatchWheelEvent(input, 0, -1);
     shouldBeEqualToString('input.value', initialValue);
     input.removeAttribute('disabled');
 
 
     debug('Read-only input element:');
     input.readOnly = true;
-    dispatchWheelEvent(input, 0, 1);
+    await dispatchWheelEvent(input, 0, -1);
     shouldBeEqualToString('input.value', initialValue);
     input.readOnly = false;
 
     debug('No focus:');
     document.getElementById('another').focus();
-    dispatchWheelEvent(input, 0, 1);
+    await dispatchWheelEvent(input, 0, -1);
     shouldBeEqualToString('input.value', initialValue);
 
     parent.parentNode.removeChild(parent);
+    finishJSTest();
 }
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event.html
index a5869d6..6dd8cbd8 100644
--- a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event.html
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/time-multiple-fields/time-multiple-fields-wheel-event.html
@@ -2,6 +2,7 @@
 <html>
 <head>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../../../resources/gesture-util.js"></script>
 <script src="../resources/common-wheel-event.js"></script>
 </head>
 <body>
diff --git a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event.html b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event.html
index 1737161..a6e65ce 100644
--- a/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event.html
+++ b/third_party/blink/web_tests/virtual/form-controls-refresh-disabled/fast/forms/week-multiple-fields/week-multiple-fields-wheel-event.html
@@ -2,6 +2,7 @@
 <html>
 <head>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../../../resources/gesture-util.js"></script>
 <script src="../resources/common-wheel-event.js"></script>
 </head>
 <body>
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 2d1c54a..9603fd54 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-10-1-125-g093f87bfe
-Revision: 093f87bfe45160195ade7bd5174bbaaf50ebd6be
+Version: VER-2-10-1-127-g3f70e6d20
+Revision: 3f70e6d20c82b28174096adcd0657b3c998b007b
 CPEPrefix: cpe:/a:freetype:freetype:2.10.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/third_party/freetype/include/freetype-custom-config/ftmodule.h b/third_party/freetype/include/freetype-custom-config/ftmodule.h
index 72eda05..7d1271f 100644
--- a/third_party/freetype/include/freetype-custom-config/ftmodule.h
+++ b/third_party/freetype/include/freetype-custom-config/ftmodule.h
@@ -23,8 +23,6 @@
 FT_USE_MODULE( FT_Renderer_Class, ft_raster1_renderer_class )
 FT_USE_MODULE( FT_Module_Class, sfnt_module_class )
 FT_USE_MODULE( FT_Renderer_Class, ft_smooth_renderer_class )
-FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcd_renderer_class )
-FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcdv_renderer_class )
 FT_USE_MODULE( FT_Module_Class, psaux_module_class )
 
 #if defined(PDFIUM_REQUIRED_MODULES)
diff --git a/third_party/freetype/include/freetype-custom-config/ftoption.h b/third_party/freetype/include/freetype-custom-config/ftoption.h
index 197b5fc..151ed6b 100644
--- a/third_party/freetype/include/freetype-custom-config/ftoption.h
+++ b/third_party/freetype/include/freetype-custom-config/ftoption.h
@@ -121,10 +121,8 @@
    * mitigate color fringes inherent to this technology, you also need to
    * explicitly set up LCD filtering.
    *
-   * Note that this feature is covered by several Microsoft patents and
-   * should not be activated in any default build of the library.  When this
-   * macro is not defined, FreeType offers alternative LCD rendering
-   * technology that produces excellent output without LCD filtering.
+   * When this macro is not defined, FreeType offers alternative LCD
+   * rendering technology that produces excellent output.
    */
 #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING
 
diff --git a/third_party/libaddressinput/OWNERS b/third_party/libaddressinput/OWNERS
index f57c6c2..7a40bb9 100644
--- a/third_party/libaddressinput/OWNERS
+++ b/third_party/libaddressinput/OWNERS
@@ -1,6 +1,6 @@
 battre@chromium.org
 dvadym@chromium.org
-koerber@chromium.org
+koerber@google.com
 kolos@chromium.org
 mathp@chromium.org
 rouslan@chromium.org
diff --git a/third_party/libphonenumber/OWNERS b/third_party/libphonenumber/OWNERS
index f57c6c2..7a40bb9 100644
--- a/third_party/libphonenumber/OWNERS
+++ b/third_party/libphonenumber/OWNERS
@@ -1,6 +1,6 @@
 battre@chromium.org
 dvadym@chromium.org
-koerber@chromium.org
+koerber@google.com
 kolos@chromium.org
 mathp@chromium.org
 rouslan@chromium.org
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 27d59a4..8671fef 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -8397,6 +8397,32 @@
   <int value="6" label="FAILED_INCLUSION_PROOF_SERVER"/>
 </enum>
 
+<enum name="CertProvisioningEvent">
+  <int value="0" label="kRegisteredToInvalidationTopic"/>
+  <int value="1" label="kInvalidationReceived"/>
+  <int value="2" label="kWorkerRetryWithoutInvalidation"/>
+  <int value="3" label="kWorkerRetrySucceededWithoutInvalidation"/>
+  <int value="4" label="kProfileRetriedManually"/>
+  <int value="5" label="kWorkerCreated"/>
+  <int value="6" label="kWorkerDeserialized"/>
+  <int value="7" label="kWorkerDeserializationFailed"/>
+</enum>
+
+<enum name="CertProvisioningWorkerState">
+  <int value="0" label="kInitState"/>
+  <int value="1" label="kKeypairGenerated"/>
+  <int value="2" label="kStartCsrResponseReceived"/>
+  <int value="3" label="kVaChallengeFinished"/>
+  <int value="4" label="kKeyRegistered"/>
+  <int value="5" label="kKeypairMarked"/>
+  <int value="6" label="kSignCsrFinished"/>
+  <int value="7" label="kFinishCsrResponseReceived"/>
+  <int value="8" label="kSucceeded"/>
+  <int value="9" label="kInconsistentDataError"/>
+  <int value="10" label="kFailed"/>
+  <int value="11" label="kCanceled"/>
+</enum>
+
 <enum name="CertVerifierTrialComparisonResult">
   <int value="0" label="Invalid"/>
   <int value="1" label="Equal"/>
@@ -38337,6 +38363,7 @@
   <int value="-1899409297"
       label="ProcessSharingWithStrictSiteInstances:disabled"/>
   <int value="-1898386671" label="PasswordCheck:enabled"/>
+  <int value="-1896871201" label="CrossOriginOpenerPolicyReporting:enabled"/>
   <int value="-1896394207" label="PasswordChange:disabled"/>
   <int value="-1895719323" label="VrBrowsingTabsView:enabled"/>
   <int value="-1894699049" label="AudioFocusEnforcement:disabled"/>
@@ -38864,6 +38891,7 @@
   <int value="-1322882747" label="disable-datasaver-prompt"/>
   <int value="-1319688939" label="ignore-gpu-blacklist"/>
   <int value="-1318914924" label="OverflowIconsForMediaControls:enabled"/>
+  <int value="-1316769004" label="CrossOriginOpenerPolicyReporting:disabled"/>
   <int value="-1314603238" label="ChromeHomePullToRefreshIphAtTop:enabled"/>
   <int value="-1313810940" label="StrictOriginIsolation:disabled"/>
   <int value="-1311575452"
@@ -39836,6 +39864,7 @@
   <int value="-203968600" label="SyncSupportSecondaryAccount:disabled"/>
   <int value="-202007318" label="AndroidAIAFetching:enabled"/>
   <int value="-200805659" label="ContextualSuggestionsButton:enabled"/>
+  <int value="-199690952" label="PageInfoV2:enabled"/>
   <int value="-196110497" label="force-text-direction"/>
   <int value="-195029497" label="MediaRemoting:disabled"/>
   <int value="-192919826" label="ViewsSimplifiedFullscreenUI:enabled"/>
@@ -40208,6 +40237,7 @@
   <int value="229476202" label="CryptAuthV2Enrollment:enabled"/>
   <int value="229877058" label="ShelfScrollable:disabled"/>
   <int value="233721248" label="DownloadRename:disabled"/>
+  <int value="237066116" label="PageInfoV2:disabled"/>
   <int value="237570976" label="CommandLineOnNonRooted:disabled"/>
   <int value="237964589"
       label="enable-manual-fallback-for-password-saving:disabled"/>
@@ -40999,6 +41029,7 @@
       label="AutofillCreditCardLastUsedDateDisplay:disabled"/>
   <int value="1155905651" label="DrawVerticallyEdgeToEdge:disabled"/>
   <int value="1155923106" label="NTPOfflinePages:enabled"/>
+  <int value="1158333871" label="QueryTilesEnableQueryEditing:enabled"/>
   <int value="1163255347" label="ash-enable-touch-view-touch-feedback"/>
   <int value="1164377197" label="SwipingFromLeftEdgeToGoBack:enabled"/>
   <int value="1164460660" label="AutofillEnableVirtualCard:enabled"/>
@@ -41559,6 +41590,7 @@
   <int value="1798347197"
       label="ContextualSuggestionsIPHReverseScroll:disabled"/>
   <int value="1799521026" label="LegacyTLSEnforced:disabled"/>
+  <int value="1802874714" label="QueryTilesEnableQueryEditing:disabled"/>
   <int value="1803465156" label="enable-zero-suggest-most-visited"/>
   <int value="1803470125" label="SyncUSSSessions:enabled"/>
   <int value="1803914892" label="TemporaryUnexpireFlagsM76:enabled"/>
@@ -45795,6 +45827,12 @@
   <int value="7"
       label="A navigation suggestion is found using target embedding against
              a top domain"/>
+  <int value="8"
+      label="A navigation suggestion is found using a skeleton match against
+             a top 500 domain"/>
+  <int value="9"
+      label="A navigation suggestion is found using a skeleton match against
+             a top 5k (but not top 500) domain"/>
 </enum>
 
 <enum name="NavigationURLScheme">
@@ -61055,6 +61093,17 @@
   </int>
 </enum>
 
+<!--
+  SharingActionSource must be kept in sync with ValidSharingActionSource defined
+  in //ui/file_manager/file_manager/foreground/js/file_manager_commands.js.
+-->
+
+<enum name="SharingActionSource">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Context Menu"/>
+  <int value="2" label="Share Button"/>
+</enum>
+
 <enum name="SharingClickToCallEntryPoint">
   <int value="0" label="Left click on a phone link"/>
   <int value="1" label="Right click on a phone link"/>
@@ -65281,7 +65330,8 @@
   <int value="3" label="Turned off and setup not completed"/>
   <int value="4" label="Must confirm sync settings"/>
   <int value="5" label="Disallowed by enterprise policy"/>
-  <int value="6" label="Disallowed by the platform"/>
+  <int value="6"
+      label="Disallowed by the platform (obsolete, was never recorded)"/>
 </enum>
 
 <enum name="SyncKeystoreDecryptionFailure">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 0efc92e..581cb78 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -24751,6 +24751,56 @@
   <summary>Records the session duration in Chrome OS camera service.</summary>
 </histogram>
 
+<histogram name="ChromeOS.CertProvisioning.CsrSignTime" units="ms"
+    expires_after="2021-06-01">
+  <owner>miersh@google.com</owner>
+  <owner>pmarko@chromium.org</owner>
+  <summary>
+    The amount of time it took to sign one CSR by certificate provisioning
+    worker.
+  </summary>
+</histogram>
+
+<histogram name="ChromeOS.CertProvisioning.Event" enum="CertProvisioningEvent"
+    expires_after="2021-06-01">
+  <owner>miersh@google.com</owner>
+  <owner>pmarko@chromium.org</owner>
+  <summary>
+    Number of times each event is reached during certificate provisioning flow.
+  </summary>
+</histogram>
+
+<histogram name="ChromeOS.CertProvisioning.KeypairGenerationTime" units="ms"
+    expires_after="2021-06-01">
+  <owner>miersh@google.com</owner>
+  <owner>pmarko@chromium.org</owner>
+  <summary>
+    The amount of time it took to generate one key pair by certificate
+    provisioning worker.
+  </summary>
+</histogram>
+
+<histogram name="ChromeOS.CertProvisioning.Result"
+    enum="CertProvisioningWorkerState" expires_after="2021-06-01">
+  <owner>miersh@google.com</owner>
+  <owner>pmarko@chromium.org</owner>
+  <summary>
+    For final states (see the list in cert_provisioning::IsFinalState): number
+    of times certificate provisioning worker finished on every state. For other
+    states: number of times the worker failed on every state.
+  </summary>
+</histogram>
+
+<histogram name="ChromeOS.CertProvisioning.VaTime" units="ms"
+    expires_after="2021-06-01">
+  <owner>miersh@google.com</owner>
+  <owner>pmarko@chromium.org</owner>
+  <summary>
+    The amount of time it took to build one Verified Access response by
+    certificate provisioning worker.
+  </summary>
+</histogram>
+
 <histogram name="ChromeOS.CWP.CollectHeap"
     enum="ChromeOSProfileCollectionStatus" expires_after="2020-02-01">
   <obsolete>
@@ -58733,6 +58783,35 @@
   </summary>
 </histogram>
 
+<histogram name="FileBrowser.Share.ActionSource" enum="SharingActionSource"
+    expires_after="2021-05-05">
+  <owner>lucmult@chromium.org</owner>
+  <owner>majewski@chromium.org</owner>
+  <summary>Chrome OS File Browser: the source of file sharing action.</summary>
+</histogram>
+
+<!-- TODO(crbug.com/1063169): record Share.AppId AppID for each entity. -->
+
+<histogram name="FileBrowser.Share.FileCount" units="files"
+    expires_after="2021-05-05">
+  <owner>lucmult@chromium.org</owner>
+  <owner>majewski@chromium.org</owner>
+  <summary>
+    Chrome OS File Browser: The number of files shared via a single Share
+    action.
+  </summary>
+</histogram>
+
+<histogram name="FileBrowser.Share.FileType" enum="ViewFileType"
+    expires_after="2021-05-05">
+  <owner>lucmult@chromium.org</owner>
+  <owner>majewski@chromium.org</owner>
+  <summary>
+    Chrome OS File Browser: The type of the file shared via Share action.
+    Recorded for each file being shared by a share action
+  </summary>
+</histogram>
+
 <histogram name="FileBrowser.SuggestApps.CloseDialog"
     enum="SuggestAppsDialogCloseReason" expires_after="M89">
   <owner>slangley@chromium.org</owner>
@@ -153150,6 +153229,9 @@
 
 <histogram name="Sharing.ClickToCallContextMenuPhoneNumberParsingDelay"
     units="microseconds" expires_after="M84">
+  <obsolete>
+    Removed in M84 after gathering enough data as a baseline.
+  </obsolete>
   <owner>himanshujaju@chromium.org</owner>
   <owner>knollr@chromium.org</owner>
   <owner>peter@chromium.org</owner>
@@ -175188,10 +175270,12 @@
 </histogram>
 
 <histogram name="UserSessionManager.RestoreOnCrash.AccountIdValid"
-    enum="BooleanValid" expires_after="2020-02-16">
-  <owner>msarda@chromium.org</owner>
+    enum="BooleanValid" expires_after="2021-02-16">
+  <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
-    The result of restoring account id on Chrome restart after crash.
+    The result of restoring account id on Chrome restart after crash on Chrome
+    OS.
   </summary>
 </histogram>
 
@@ -189677,6 +189761,16 @@
   <affected-histogram name="DiskBasedCertCache.CertIo"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="CertProvisioningScope" separator=".">
+  <suffix name="Device" label=""/>
+  <suffix name="User" label=""/>
+  <affected-histogram name="ChromeOS.CertProvisioning.CsrSignTime"/>
+  <affected-histogram name="ChromeOS.CertProvisioning.Event"/>
+  <affected-histogram name="ChromeOS.CertProvisioning.KeypairGenerationTime"/>
+  <affected-histogram name="ChromeOS.CertProvisioning.Result"/>
+  <affected-histogram name="ChromeOS.CertProvisioning.VaTime"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="CertVerifyProcImpl" separator=".">
   <suffix name="Android" label="CertVerifyProcAndroid"/>
   <suffix name="Builtin" label="CertVerifyProcBuiltin"/>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 92de396..a2d88c1a 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -93,7 +93,7 @@
 # This is an opt-in list for tester which will skip the perf data handling.
 # The perf data will be handled on a separated 'processor' VM.
 # This list will be removed or replace by an opt-out list.
-LIGHTWEIGHT_TESTERS = ['linux-perf-fyi']
+LIGHTWEIGHT_TESTERS = ['linux-perf-fyi', 'android-pixel2-perf-fyi']
 
 FYI_BUILDERS = {
     'android-nexus5x-perf-fyi': {
@@ -209,6 +209,10 @@
         'platform': 'linux',
         'perf_processor': True,
     },
+    'android-pixel2-processor-perf-fyi': {
+        'platform': 'linux',
+        'perf_processor': True,
+    },
 }
 
 # These configurations are taken from chromium_perf.py in
@@ -1085,6 +1089,7 @@
   if 'additional_compile_targets' in condensed_config:
     config['additional_compile_targets'] = (
         condensed_config['additional_compile_targets'])
+  # TODO(crbug.com/1078675): remove this setting
   if 'perf_processor' in condensed_config:
     config['merge'] = {
         'script': '//tools/perf/process_perf_results.py',
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index 3da634f4..7f30023e 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -2971,13 +2971,13 @@
   //
   // root
   // |
-  // paragraph________________________
-  // |               |                |
-  // static_text     link             search input
-  // |               |                |
-  // text_node       static_text      text_node
-  //                 |
-  //                 text_node
+  // paragraph_________________________________________
+  // |               |                |                |
+  // static_text     link             search input     pdf_highlight
+  // |               |                |                |
+  // text_node       static_text      text_node        static_text
+  //                 |                                 |
+  //                 text_node                         text_node
 
   ui::AXNodeData root_data;
   root_data.id = 1;
@@ -3024,6 +3024,21 @@
   search_text.SetName("placeholder");
   search_box.child_ids.push_back(search_text.id);
 
+  ui::AXNodeData pdf_highlight_data;
+  pdf_highlight_data.id = 10;
+  pdf_highlight_data.role = ax::mojom::Role::kPdfActionableHighlight;
+  paragraph_data.child_ids.push_back(pdf_highlight_data.id);
+
+  ui::AXNodeData static_text_data3;
+  static_text_data3.id = 11;
+  static_text_data3.role = ax::mojom::Role::kStaticText;
+  pdf_highlight_data.child_ids.push_back(static_text_data3.id);
+
+  ui::AXNodeData inline_text_data3;
+  inline_text_data3.id = 12;
+  inline_text_data3.role = ax::mojom::Role::kInlineTextBox;
+  static_text_data3.child_ids.push_back(inline_text_data3.id);
+
   ui::AXTreeUpdate update;
   ui::AXTreeData tree_data;
   tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
@@ -3039,6 +3054,9 @@
   update.nodes.push_back(inline_text_data2);
   update.nodes.push_back(search_box);
   update.nodes.push_back(search_text);
+  update.nodes.push_back(pdf_highlight_data);
+  update.nodes.push_back(static_text_data3);
+  update.nodes.push_back(inline_text_data3);
 
   Init(update);
 
@@ -3051,6 +3069,9 @@
   AXNode* inline_text_node2 = static_text_node2->children()[0];
   AXNode* search_box_node = paragraph_node->children()[2];
   AXNode* search_text_node = search_box_node->children()[0];
+  AXNode* pdf_highlight_node = paragraph_node->children()[3];
+  AXNode* static_text_node3 = pdf_highlight_node->children()[0];
+  AXNode* inline_text_node3 = static_text_node3->children()[0];
 
   ComPtr<IRawElementProviderSimple> link_node_raw =
       QueryInterfaceFromNode<IRawElementProviderSimple>(link_node);
@@ -3064,6 +3085,10 @@
       QueryInterfaceFromNode<IRawElementProviderSimple>(search_box_node);
   ComPtr<IRawElementProviderSimple> search_text_node_raw =
       QueryInterfaceFromNode<IRawElementProviderSimple>(search_text_node);
+  ComPtr<IRawElementProviderSimple> pdf_highlight_node_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(pdf_highlight_node);
+  ComPtr<IRawElementProviderSimple> inline_text_node_raw3 =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(inline_text_node3);
 
   // Test GetEnclosingElement for the two leaves text nodes. The enclosing
   // element of the first one should be its static text parent (because inline
@@ -3106,6 +3131,18 @@
   EXPECT_HRESULT_SUCCEEDED(
       text_range_provider->GetEnclosingElement(&enclosing_element));
   EXPECT_EQ(search_box_node_raw.Get(), enclosing_element.Get());
+
+  // The enclosing element for the text node that is grandchild of the
+  // pdf_highlight node should return the pdf_highlight node.
+  EXPECT_HRESULT_SUCCEEDED(inline_text_node_raw3->GetPatternProvider(
+      UIA_TextPatternId, &text_provider));
+
+  EXPECT_HRESULT_SUCCEEDED(
+      text_provider->get_DocumentRange(&text_range_provider));
+
+  EXPECT_HRESULT_SUCCEEDED(
+      text_range_provider->GetEnclosingElement(&enclosing_element));
+  EXPECT_EQ(pdf_highlight_node_raw.Get(), enclosing_element.Get());
 }
 
 TEST_F(AXPlatformNodeTextRangeProviderTest,
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index fd242e19..2d93a5f 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -6800,12 +6800,7 @@
   // UIA provides multiple "views": raw, content and control. We only want to
   // populate the content and control views with items that make sense to
   // traverse over.
-
   if (GetDelegate()->IsWebContent()) {
-    // Invisible or ignored elements should not show up in control view at all.
-    if (IsInvisibleOrIgnored())
-      return false;
-
     if (IsTextOnlyObject()) {
       // A text leaf can be a UIAControl, but text inside of a heading, link,
       // button, etc. where the role allows the name to be generated from the
@@ -6845,8 +6840,7 @@
         }
         parent = FromNativeViewAccessible(parent->GetParent());
       }
-    }  // end of text only case.
-
+    }
     const AXNodeData& data = GetData();
     // https://docs.microsoft.com/en-us/windows/win32/winauto/uiauto-treeoverview#control-view
     // The control view also includes noninteractive UI items that contribute
@@ -6898,10 +6892,9 @@
         !data.HasState(ax::mojom::State::kFocusable) && !data.IsClickable()) {
       return false;
     }
-
     return true;
-  }  // end of web-content only case.
-
+  }
+  // non web-content case.
   const AXNodeData& data = GetData();
   return !((IsReadOnlySupported(data.role) && data.IsReadOnlyOrDisabled()) ||
            data.HasState(ax::mojom::State::kInvisible) ||
@@ -6978,8 +6971,13 @@
     // TODO(bebeaudr): We might be able to remove ax::mojom::Role::kLink once
     // http://crbug.com/1054514 is fixed. Links should not have to hide their
     // children.
+    // TODO(virens): |kPdfActionableHighlight| needs to follow a fix similar to
+    // links. At present Pdf highlghts have text nodes as children. But, we may
+    // enable pdf highlights to have complex children like links based on user
+    // feedback.
     case ax::mojom::Role::kLink:
     case ax::mojom::Role::kTextField:
+    case ax::mojom::Role::kPdfActionableHighlight:
       return true;
     default:
       return false;
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index 4a43009..a848ea1 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -3823,93 +3823,6 @@
                      UIA_IsDialogPropertyId, true);
 }
 
-TEST_F(AXPlatformNodeWinTest,
-       UIAGetPropertyValueIsControlElementIgnoredInvisible) {
-  AXNodeData root;
-  root.id = 1;
-  root.role = ax::mojom::Role::kRootWebArea;
-  root.child_ids = {2, 3, 4, 5, 6, 7, 8};
-
-  AXNodeData normal_button;
-  normal_button.id = 2;
-  normal_button.role = ax::mojom::Role::kButton;
-
-  AXNodeData ignored_button;
-  ignored_button.id = 3;
-  ignored_button.role = ax::mojom::Role::kButton;
-  ignored_button.AddState(ax::mojom::State::kIgnored);
-
-  AXNodeData invisible_button;
-  invisible_button.id = 4;
-  invisible_button.role = ax::mojom::Role::kButton;
-  invisible_button.AddState(ax::mojom::State::kInvisible);
-
-  AXNodeData invisible_focusable_button;
-  invisible_focusable_button.id = 5;
-  invisible_focusable_button.role = ax::mojom::Role::kButton;
-  invisible_focusable_button.AddState(ax::mojom::State::kInvisible);
-  invisible_focusable_button.AddState(ax::mojom::State::kFocusable);
-
-  AXNodeData focusable_generic_container;
-  focusable_generic_container.id = 6;
-  focusable_generic_container.role = ax::mojom::Role::kGenericContainer;
-  focusable_generic_container.AddState(ax::mojom::State::kFocusable);
-
-  AXNodeData ignored_focusable_generic_container;
-  ignored_focusable_generic_container.id = 7;
-  ignored_focusable_generic_container.role = ax::mojom::Role::kGenericContainer;
-  ignored_focusable_generic_container.AddState(ax::mojom::State::kIgnored);
-  focusable_generic_container.AddState(ax::mojom::State::kFocusable);
-
-  AXNodeData invisible_focusable_generic_container;
-  invisible_focusable_generic_container.id = 8;
-  invisible_focusable_generic_container.role =
-      ax::mojom::Role::kGenericContainer;
-  invisible_focusable_generic_container.AddState(ax::mojom::State::kInvisible);
-  invisible_focusable_generic_container.AddState(ax::mojom::State::kFocusable);
-
-  Init(root, normal_button, ignored_button, invisible_button,
-       invisible_focusable_button, focusable_generic_container,
-       ignored_focusable_generic_container,
-       invisible_focusable_generic_container);
-
-  // Turn on web content mode for the AXTree.
-  TestAXNodeWrapper::SetGlobalIsWebContent(true);
-
-  // Normal button (id=2), no invisible or ignored state set. Should be a
-  // control element.
-  EXPECT_UIA_BOOL_EQ(GetIRawElementProviderSimpleFromChildIndex(0),
-                     UIA_IsControlElementPropertyId, true);
-
-  // Button with ignored state (id=3). Should not be a control element.
-  EXPECT_UIA_BOOL_EQ(GetIRawElementProviderSimpleFromChildIndex(1),
-                     UIA_IsControlElementPropertyId, false);
-
-  // Button with invisible state (id=4). Should not be a control element.
-  EXPECT_UIA_BOOL_EQ(GetIRawElementProviderSimpleFromChildIndex(2),
-                     UIA_IsControlElementPropertyId, false);
-
-  // Button with invisible state, but focusable (id=5). Should not be a control
-  // element.
-  EXPECT_UIA_BOOL_EQ(GetIRawElementProviderSimpleFromChildIndex(3),
-                     UIA_IsControlElementPropertyId, false);
-
-  // Generic container, focusable (id=6). Should be a control
-  // element.
-  EXPECT_UIA_BOOL_EQ(GetIRawElementProviderSimpleFromChildIndex(4),
-                     UIA_IsControlElementPropertyId, true);
-
-  // Generic container, ignored but focusable (id=7). Should not be a control
-  // element.
-  EXPECT_UIA_BOOL_EQ(GetIRawElementProviderSimpleFromChildIndex(5),
-                     UIA_IsControlElementPropertyId, false);
-
-  // Generic container, invisible and ignored, but focusable (id=8). Should not
-  // be a control element.
-  EXPECT_UIA_BOOL_EQ(GetIRawElementProviderSimpleFromChildIndex(6),
-                     UIA_IsControlElementPropertyId, false);
-}
-
 TEST_F(AXPlatformNodeWinTest, TestUIAGetControllerForPropertyId) {
   AXNodeData root;
   root.id = 1;
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index a01b496d..17706b1 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -383,6 +383,15 @@
   externs_list = [ "//ui/file_manager/externs/command_handler_deps.js" ]
 }
 
+js_unittest("file_manager_commands_unittest") {
+  deps = [
+    ":file_manager_commands",
+    ":file_tasks",
+    "//ui/file_manager/file_manager/background/js:mock_volume_manager",
+    "//ui/webui/resources/js:webui_resource_test",
+  ]
+}
+
 js_library("file_selection") {
   deps = [
     ":constants",
@@ -798,6 +807,7 @@
   deps = [
     ":actions_model_unittest",
     ":file_list_model_unittest",
+    ":file_manager_commands_unittest",
     ":file_tasks_unittest",
     ":file_transfer_controller_unittest",
     ":import_controller_unittest",
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index d657153..5cd0ed33 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -32,6 +32,58 @@
 const CommandUtil = {};
 
 /**
+ * Possible share action sources for UMA.
+ * @enum {string}
+ * @const
+ */
+CommandUtil.SharingActionSourceForUMA = {
+  UNKNOWN: 'Unknown',
+  CONTEXT_MENU: 'Context Menu',
+  SHARE_BUTTON: 'Share Button',
+};
+
+/**
+ * The IDs of elements that can trigger share action.
+ * @enum {string}
+ * @const
+ */
+CommandUtil.SharingActionElementId = {
+  CONTEXT_MENU: 'file-list',
+  SHARE_BUTTON: 'share-menu-button',
+};
+
+/**
+ * A list of supported values for SharingActionSource enum. Keep this in sync
+ * with SharingActionSource defined in //tools/metrics/histograms/enums.xml.
+ */
+CommandUtil.ValidSharingActionSource = [
+  CommandUtil.SharingActionSourceForUMA.UNKNOWN,
+  CommandUtil.SharingActionSourceForUMA.CONTEXT_MENU,
+  CommandUtil.SharingActionSourceForUMA.SHARE_BUTTON,
+];
+
+/**
+ * Helper function that for the given event returns the source of a share
+ * action. If the source cannot be determined, this function returns
+ * CommandUtil.SharingActionSourceForUMA.UNKNOWN.
+ * @param {!Event} event The event that triggered share action.
+ * @return {!CommandUtil.SharingActionSourceForUMA}
+ */
+CommandUtil.getSharingActionSource = event => {
+  const id = event.target.id;
+  switch (id) {
+    case CommandUtil.SharingActionElementId.CONTEXT_MENU:
+      return CommandUtil.SharingActionSourceForUMA.CONTEXT_MENU;
+    case CommandUtil.SharingActionElementId.SHARE_BUTTON:
+      return CommandUtil.SharingActionSourceForUMA.SHARE_BUTTON;
+    default: {
+      console.error('Unrecognized event.target.id for sharing action "%s"', id);
+      return CommandUtil.SharingActionSourceForUMA.UNKNOWN;
+    }
+  }
+};
+
+/**
  * Extracts entry on which command event was dispatched.
  *
  * @param {!CommandHandlerDeps} fileManager
@@ -554,6 +606,24 @@
 };
 
 /**
+ * Records UMA statistics about Share action.
+ * @param {!Event} event The event that triggered sharing action.
+ * @param {!Array<!FileEntry>} entries File entries to be shared.
+ */
+CommandHandler.recordSharingAction = (event, entries) => {
+  const source = CommandUtil.getSharingActionSource(event);
+  metrics.recordEnum(
+      'Share.ActionSource', source, CommandUtil.ValidSharingActionSource);
+  metrics.recordSmallCount('Share.FileCount', entries.length);
+  for (const entry of entries) {
+    metrics.recordEnum(
+        'Share.FileType', FileTasks.getViewFileType(entry),
+        FileTasks.UMA_INDEX_KNOWN_EXTENSIONS);
+  }
+  // TODO(crbug.com/1063169): record Share.AppId AppID for each entity.
+};
+
+/**
  * Commands.
  * @private @const {Object<Command>}
  */
@@ -1751,6 +1821,7 @@
 CommandHandler.COMMANDS_['share'] = new class extends Command {
   execute(event, fileManager) {
     const entries = CommandUtil.getCommandEntries(fileManager, event.target);
+    CommandHandler.recordSharingAction(event, entries);
     const actionsController = fileManager.actionsController;
 
     fileManager.actionsController.getActionsForEntries(entries).then(
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands_unittest.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands_unittest.js
new file mode 100644
index 0000000..4a2728fb
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands_unittest.js
@@ -0,0 +1,94 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * Utility function that appends value under a given name in the store.
+ * @param {!Map<string, !Array<string|number>>} store
+ * @param {string} name
+ * @param {string|number} value
+ */
+function record(store, name, value) {
+  let recorded = store.get(name);
+  if (!recorded) {
+    recorded = [];
+    store.set(name, recorded);
+  }
+  recorded.push(value);
+}
+
+/**
+ * Checks that a correct sharing action source is extracted from an event.
+ */
+function testGetSharingActionSource() {
+  const testData = [
+    {
+      event: {target: {id: CommandUtil.SharingActionElementId.CONTEXT_MENU}},
+      expected: CommandUtil.SharingActionSourceForUMA.CONTEXT_MENU,
+    },
+    {
+      event: {target: {id: CommandUtil.SharingActionElementId.SHARE_BUTTON}},
+      expected: CommandUtil.SharingActionSourceForUMA.SHARE_BUTTON,
+    },
+    {
+      event: {target: {id: '__no_such_id__'}},
+      expected: CommandUtil.SharingActionSourceForUMA.UNKNOWN,
+    },
+    {
+      event: {target: {id: null}},
+      expected: CommandUtil.SharingActionSourceForUMA.UNKNOWN,
+    },
+  ];
+  for (const data of testData) {
+    const source = CommandUtil.getSharingActionSource(data.event);
+    assertEquals(data.expected, source);
+  }
+}
+
+/**
+ * Checks that we are correctly recording UMA about Share action.
+ */
+function testReportSharingAction() {
+  // Setup: create a fake metrics object that can be examined for content.
+  const enumMap = new Map();
+  const countMap = new Map();
+  window.metrics = {
+    recordEnum: (name, value, valid) => {
+      assertTrue(valid.includes(value));
+      record(enumMap, name, value);
+    },
+    recordSmallCount: (name, value) => {
+      record(countMap, name, value);
+    },
+  };
+  const mockFileSystem = new MockFileSystem('volumeId');
+
+  // Actual tests.
+  CommandHandler.recordSharingAction(
+      /** @type {!Event} */ (
+          {target: {id: CommandUtil.SharingActionElementId.CONTEXT_MENU}}),
+      [
+        MockFileEntry.create(mockFileSystem, '/test.log'),
+        MockFileEntry.create(mockFileSystem, '/test.doc'),
+        MockFileEntry.create(mockFileSystem, '/test.__no_such_extension__'),
+      ]);
+  assertArrayEquals(
+      enumMap.get('Share.ActionSource'),
+      [CommandUtil.SharingActionSourceForUMA.CONTEXT_MENU]);
+  assertArrayEquals(countMap.get('Share.FileCount'), [3]);
+  assertArrayEquals(enumMap.get('Share.FileType'), ['.log', '.doc', 'other']);
+
+  CommandHandler.recordSharingAction(
+      /** @type {!Event} */ (
+          {target: {id: CommandUtil.SharingActionElementId.SHARE_BUTTON}}),
+      [
+        MockFileEntry.create(mockFileSystem, '/test.log'),
+      ]);
+  assertArrayEquals(enumMap.get('Share.ActionSource'), [
+    CommandUtil.SharingActionSourceForUMA.CONTEXT_MENU,
+    CommandUtil.SharingActionSourceForUMA.SHARE_BUTTON,
+  ]);
+  assertArrayEquals(countMap.get('Share.FileCount'), [3, 1]);
+  assertArrayEquals(
+      enumMap.get('Share.FileType'), ['.log', '.doc', 'other', '.log']);
+}
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks.js b/ui/file_manager/file_manager/foreground/js/file_tasks.js
index 8c764d15b..4149326 100644
--- a/ui/file_manager/file_manager/foreground/js/file_tasks.js
+++ b/ui/file_manager/file_manager/foreground/js/file_tasks.js
@@ -240,6 +240,19 @@
   }
 
   /**
+   * Returns ViewFileType enum or 'other' for the given entry.
+   * @param {!Entry} entry The entry for which ViewFileType is computed.
+   * @return {string} A ViewFileType enum or 'other'.
+   */
+  static getViewFileType(entry) {
+    let extension = FileType.getExtension(entry).toLowerCase();
+    if (FileTasks.UMA_INDEX_KNOWN_EXTENSIONS.indexOf(extension) < 0) {
+      extension = 'other';
+    }
+    return extension;
+  }
+
+  /**
    * Records trial of opening file grouped by extensions.
    *
    * @param {!VolumeManager} volumeManager
@@ -248,13 +261,9 @@
    */
   static recordViewingFileTypeUMA_(volumeManager, entries) {
     for (let i = 0; i < entries.length; i++) {
-      const entry = entries[i];
-      let extension = FileType.getExtension(entry).toLowerCase();
-      if (FileTasks.UMA_INDEX_KNOWN_EXTENSIONS.indexOf(extension) < 0) {
-        extension = 'other';
-      }
       FileTasks.recordEnumWithOnlineAndOffline_(
-          volumeManager, 'ViewingFileType', extension,
+          volumeManager, 'ViewingFileType',
+          FileTasks.getViewFileType(entries[i]),
           FileTasks.UMA_INDEX_KNOWN_EXTENSIONS);
     }
   }
@@ -1215,7 +1224,7 @@
  *
  * The list must also match the FileBrowser ViewFileType entry in enums.xml.
  *
- * @const {Array<string>}
+ * @const {!Array<string>}
  */
 FileTasks.UMA_INDEX_KNOWN_EXTENSIONS = Object.freeze([
   'other',     '.3ga',         '.3gp',
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js b/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js
index cc99b2f..faeea81 100644
--- a/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js
@@ -611,3 +611,21 @@
 
   reportPromise(showImportCrostiniImageDialogIsCalled([mockEntry]), callback);
 }
+
+/**
+ * Checks that the function that returns a file type for file entry handles
+ * correctly identifies files with known and unknown extensions.
+ */
+function testGetViewFileType() {
+  const mockFileSystem = new MockFileSystem('volumeId');
+  const testData = [
+    {extension: 'log', expected: '.log'},
+    {extension: '__unknown_extension__', expected: 'other'},
+  ];
+  for (const data of testData) {
+    const mockEntry =
+        MockFileEntry.create(mockFileSystem, `/report.${data.extension}`);
+    const type = FileTasks.getViewFileType(mockEntry);
+    assertEquals(data.expected, type);
+  }
+}
diff --git a/ui/file_manager/file_manager/foreground/js/ui/search_box.js b/ui/file_manager/file_manager/foreground/js/ui/search_box.js
index ef22efec..100824a 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/search_box.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/search_box.js
@@ -108,7 +108,7 @@
     element.parentNode.appendChild(this.autocompleteList);
   }
 
-  /** @private {boolean} */
+  /** @return {boolean} */
   get collapsed() {
     return this.searchWrapper.hasAttribute('collapsed');
   }
diff --git a/ui/views/focus/focus_manager.cc b/ui/views/focus/focus_manager.cc
index 8978d632..6acdf0e 100644
--- a/ui/views/focus/focus_manager.cc
+++ b/ui/views/focus/focus_manager.cc
@@ -17,6 +17,7 @@
 #include "ui/base/ime/text_input_client.h"
 #include "ui/events/event.h"
 #include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/focus/focus_manager_delegate.h"
 #include "ui/views/focus/focus_search.h"
 #include "ui/views/focus/widget_focus_manager.h"
@@ -522,7 +523,14 @@
 bool FocusManager::ProcessAccelerator(const ui::Accelerator& accelerator) {
   if (accelerator_manager_.Process(accelerator))
     return true;
-  return delegate_ && delegate_->ProcessAccelerator(accelerator);
+  if (delegate_ && delegate_->ProcessAccelerator(accelerator))
+    return true;
+  return RedirectAcceleratorToBubbleAnchorWidget(accelerator);
+}
+
+bool FocusManager::IsAcceleratorRegistered(
+    const ui::Accelerator& accelerator) const {
+  return accelerator_manager_.IsRegistered(accelerator);
 }
 
 bool FocusManager::HasPriorityHandler(
@@ -591,4 +599,25 @@
   SetFocusedView(nullptr);
 }
 
+bool FocusManager::RedirectAcceleratorToBubbleAnchorWidget(
+    const ui::Accelerator& accelerator) {
+  Widget* anchor_widget = GetBubbleAnchorWidget();
+  if (!anchor_widget)
+    return false;
+
+  FocusManager* focus_manager = anchor_widget->GetFocusManager();
+  if (!focus_manager->IsAcceleratorRegistered(accelerator))
+    return false;
+
+  // The parent view must be focused for it to process events.
+  focus_manager->SetFocusedView(anchor_widget->GetRootView());
+  return focus_manager->ProcessAccelerator(accelerator);
+}
+
+Widget* FocusManager::GetBubbleAnchorWidget() {
+  BubbleDialogDelegateView* widget_delegate =
+      widget_->widget_delegate()->AsBubbleDialogDelegate();
+  return widget_delegate ? widget_delegate->anchor_widget() : nullptr;
+}
+
 }  // namespace views
diff --git a/ui/views/focus/focus_manager.h b/ui/views/focus/focus_manager.h
index c09ee132..8c7888ae 100644
--- a/ui/views/focus/focus_manager.h
+++ b/ui/views/focus/focus_manager.h
@@ -256,6 +256,9 @@
   void AddFocusChangeListener(FocusChangeListener* listener);
   void RemoveFocusChangeListener(FocusChangeListener* listener);
 
+  // Whether the given |accelerator| is registered.
+  bool IsAcceleratorRegistered(const ui::Accelerator& accelerator) const;
+
   // Whether the given |accelerator| has a priority handler associated with it.
   bool HasPriorityHandler(const ui::Accelerator& accelerator) const;
 
@@ -328,6 +331,14 @@
   // ViewObserver:
   void OnViewIsDeleting(View* view) override;
 
+  // Try to redirect the accelerator to bubble's anchor widget to process it if
+  // the bubble didn't.
+  bool RedirectAcceleratorToBubbleAnchorWidget(
+      const ui::Accelerator& accelerator);
+
+  // Returns bubble's anchor widget.
+  Widget* GetBubbleAnchorWidget();
+
   // Whether arrow key traversal is enabled globally.
   static bool arrow_key_traversal_enabled_;
 
diff --git a/ui/views/focus/focus_manager_unittest.cc b/ui/views/focus/focus_manager_unittest.cc
index d7a64896e..b4438b0 100644
--- a/ui/views/focus/focus_manager_unittest.cc
+++ b/ui/views/focus/focus_manager_unittest.cc
@@ -54,6 +54,9 @@
     SetID(view_id);
   }
 
+  SimpleTestView(const SimpleTestView&) = delete;
+  SimpleTestView& operator=(const SimpleTestView&) = delete;
+
   void OnFocus() override {
     event_list_->push_back({
         ON_FOCUS,
@@ -72,8 +75,6 @@
 
  private:
   std::vector<FocusTestEvent>* event_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(SimpleTestView);
 };
 
 // Tests that the appropriate Focus related methods are called when a View
@@ -379,6 +380,11 @@
                                      FocusManager* focus_manager)
       : accelerator_(accelerator), focus_manager_(focus_manager) {}
 
+  SelfUnregisteringAcceleratorTarget(
+      const SelfUnregisteringAcceleratorTarget&) = delete;
+  SelfUnregisteringAcceleratorTarget& operator=(
+      const SelfUnregisteringAcceleratorTarget&) = delete;
+
   // ui::TestAcceleratorTarget:
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override {
     focus_manager_->UnregisterAccelerator(accelerator, this);
@@ -388,8 +394,6 @@
  private:
   ui::Accelerator accelerator_;
   FocusManager* focus_manager_;
-
-  DISALLOW_COPY_AND_ASSIGN(SelfUnregisteringAcceleratorTarget);
 };
 
 TEST_F(FocusManagerTest, CallsSelfDeletingAcceleratorTarget) {
@@ -438,20 +442,22 @@
         : FocusManager(widget, nullptr /* delegate */),
           dtor_tracker_(dtor_tracker) {}
 
+    FocusManagerDtorTracked(const FocusManagerDtorTracked&) = delete;
+    FocusManagerDtorTracked& operator=(const FocusManagerDtorTracked&) = delete;
+
     ~FocusManagerDtorTracked() override {
       dtor_tracker_->push_back("FocusManagerDtorTracked");
     }
 
     DtorTrackVector* dtor_tracker_;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(FocusManagerDtorTracked);
   };
 
   class TestFocusManagerFactory : public FocusManagerFactory {
    public:
     explicit TestFocusManagerFactory(DtorTrackVector* dtor_tracker)
         : dtor_tracker_(dtor_tracker) {}
+    TestFocusManagerFactory(const TestFocusManagerFactory&) = delete;
+    TestFocusManagerFactory& operator=(const TestFocusManagerFactory&) = delete;
     ~TestFocusManagerFactory() override = default;
 
     std::unique_ptr<FocusManager> CreateFocusManager(Widget* widget) override {
@@ -460,8 +466,6 @@
 
    private:
     DtorTrackVector* dtor_tracker_;
-
-    DISALLOW_COPY_AND_ASSIGN(TestFocusManagerFactory);
   };
 
   class WindowDtorTracked : public Widget {
@@ -506,6 +510,11 @@
  public:
   FocusInAboutToRequestFocusFromTabTraversalView() = default;
 
+  FocusInAboutToRequestFocusFromTabTraversalView(
+      const FocusInAboutToRequestFocusFromTabTraversalView&) = delete;
+  FocusInAboutToRequestFocusFromTabTraversalView& operator=(
+      const FocusInAboutToRequestFocusFromTabTraversalView&) = delete;
+
   void set_view_to_focus(View* view) { view_to_focus_ = view; }
 
   void AboutToRequestFocusFromTabTraversal(bool reverse) override {
@@ -514,8 +523,6 @@
 
  private:
   views::View* view_to_focus_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(FocusInAboutToRequestFocusFromTabTraversalView);
 };
 }  // namespace
 
@@ -643,6 +650,10 @@
       public testing::WithParamInterface<bool> {
  public:
   FocusManagerArrowKeyTraversalTest() = default;
+  FocusManagerArrowKeyTraversalTest(const FocusManagerArrowKeyTraversalTest&) =
+      delete;
+  FocusManagerArrowKeyTraversalTest& operator=(
+      const FocusManagerArrowKeyTraversalTest&) = delete;
   ~FocusManagerArrowKeyTraversalTest() override = default;
 
   // FocusManagerTest overrides:
@@ -671,8 +682,6 @@
   base::test::ScopedRestoreICUDefaultLocale restore_locale_;
 
   bool previous_arrow_key_traversal_enabled_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(FocusManagerArrowKeyTraversalTest);
 };
 
 // Instantiate the Boolean which is used to toggle RTL in
@@ -852,6 +861,9 @@
 class AdvanceFocusWidgetDelegate : public WidgetDelegate {
  public:
   explicit AdvanceFocusWidgetDelegate(Widget* widget) : widget_(widget) {}
+  AdvanceFocusWidgetDelegate(const AdvanceFocusWidgetDelegate&) = delete;
+  AdvanceFocusWidgetDelegate& operator=(const AdvanceFocusWidgetDelegate&) =
+      delete;
   ~AdvanceFocusWidgetDelegate() override = default;
 
   // WidgetDelegate:
@@ -860,8 +872,6 @@
 
  private:
   Widget* widget_;
-
-  DISALLOW_COPY_AND_ASSIGN(AdvanceFocusWidgetDelegate);
 };
 
 class TestBubbleDialogDelegateView : public BubbleDialogDelegateView {
@@ -870,8 +880,23 @@
       : BubbleDialogDelegateView(anchor, BubbleBorder::NONE) {
     DialogDelegate::SetButtons(ui::DIALOG_BUTTON_NONE);
   }
+  TestBubbleDialogDelegateView(const TestBubbleDialogDelegateView&) = delete;
+  TestBubbleDialogDelegateView& operator=(const TestBubbleDialogDelegateView&) =
+      delete;
   ~TestBubbleDialogDelegateView() override = default;
 
+  static TestBubbleDialogDelegateView* CreateAndShowBubble(View* anchor) {
+    TestBubbleDialogDelegateView* bubble =
+        new TestBubbleDialogDelegateView(anchor);
+    Widget* bubble_widget = BubbleDialogDelegateView::CreateBubble(bubble);
+    bubble_widget->SetFocusTraversableParent(
+        bubble->anchor_widget()->GetFocusTraversable());
+    bubble_widget->SetFocusTraversableParentView(anchor);
+    bubble->set_close_on_deactivate(false);
+    bubble_widget->Show();
+    return bubble;
+  }
+
   // If this is called, the bubble will be forced to use a NativeWidgetAura.
   // If not set, it might get a DesktopNativeWidgetAura depending on the
   // platform and other factors.
@@ -890,8 +915,6 @@
 
  private:
   bool use_native_widget_aura_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(TestBubbleDialogDelegateView);
 };
 
 }  // namespace
@@ -975,21 +998,15 @@
   parent3->AddChildView(new View());
 
   BubbleDialogDelegateView* bubble_delegate =
-      new TestBubbleDialogDelegateView(parent3);
-  test::WidgetTest::WidgetAutoclosePtr bubble_widget(
-      BubbleDialogDelegateView::CreateBubble(bubble_delegate));
-  bubble_widget->SetFocusTraversableParent(
-      bubble_delegate->anchor_widget()->GetFocusTraversable());
+      TestBubbleDialogDelegateView::CreateAndShowBubble(parent3);
+  Widget* bubble_widget = bubble_delegate->GetWidget();
 
-  bubble_widget->SetFocusTraversableParentView(parent3);
   View* child1 = new View();
   View* child2 = new View();
   child1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
   child2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
   bubble_widget->GetRootView()->AddChildView(child1);
   bubble_widget->GetRootView()->AddChildView(child2);
-  bubble_delegate->set_close_on_deactivate(false);
-  bubble_widget->Show();
 
   parent1->RequestFocus();
 
@@ -1039,20 +1056,15 @@
   GetWidget()->GetRootView()->AddChildView(parent4);
 
   BubbleDialogDelegateView* bubble_delegate =
-      new TestBubbleDialogDelegateView(parent_group);
-  test::WidgetTest::WidgetAutoclosePtr bubble_widget(
-      BubbleDialogDelegateView::CreateBubble(bubble_delegate));
-  bubble_widget->SetFocusTraversableParent(
-      bubble_delegate->anchor_widget()->GetFocusTraversable());
-  bubble_widget->SetFocusTraversableParentView(parent_group);
+      TestBubbleDialogDelegateView::CreateAndShowBubble(parent3);
+  Widget* bubble_widget = bubble_delegate->GetWidget();
+
   View* child1 = new View();
   View* child2 = new View();
   child1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
   child2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
   bubble_widget->GetRootView()->AddChildView(child1);
   bubble_widget->GetRootView()->AddChildView(child2);
-  bubble_delegate->set_close_on_deactivate(false);
-  bubble_widget->Show();
 
   parent1->RequestFocus();
 
@@ -1088,14 +1100,8 @@
   View* anchor = pane->AddChildView(std::make_unique<View>());
   anchor->SetFocusBehavior(View::FocusBehavior::ALWAYS);
 
-  BubbleDialogDelegateView* bubble = new TestBubbleDialogDelegateView(anchor);
-  test::WidgetTest::WidgetAutoclosePtr bubble_widget(
-      BubbleDialogDelegateView::CreateBubble(bubble));
-  bubble_widget->SetFocusTraversableParent(
-      bubble->anchor_widget()->GetFocusTraversable());
-  bubble_widget->SetFocusTraversableParentView(anchor);
-  bubble->set_close_on_deactivate(false);
-  bubble_widget->Show();
+  BubbleDialogDelegateView* bubble =
+      TestBubbleDialogDelegateView::CreateAndShowBubble(anchor);
 
   // We need a focusable view inside our bubble to check that focus traverses
   // in.
@@ -1118,6 +1124,9 @@
 class DesktopWidgetFocusManagerTest : public FocusManagerTest {
  public:
   DesktopWidgetFocusManagerTest() = default;
+  DesktopWidgetFocusManagerTest(const DesktopWidgetFocusManagerTest&) = delete;
+  DesktopWidgetFocusManagerTest& operator=(
+      const DesktopWidgetFocusManagerTest&) = delete;
   ~DesktopWidgetFocusManagerTest() override = default;
 
   // FocusManagerTest:
@@ -1125,9 +1134,6 @@
     set_native_widget_type(NativeWidgetType::kDesktop);
     FocusManagerTest::SetUp();
   }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DesktopWidgetFocusManagerTest);
 };
 
 TEST_F(DesktopWidgetFocusManagerTest, AnchoredDialogInDesktopNativeWidgetAura) {
@@ -1149,18 +1155,13 @@
   widget.GetRootView()->AddChildView(parent2);
 
   TestBubbleDialogDelegateView* bubble_delegate =
-      new TestBubbleDialogDelegateView(parent2);
+      TestBubbleDialogDelegateView::CreateAndShowBubble(parent2);
+  Widget* bubble_widget = bubble_delegate->GetWidget();
   bubble_delegate->UseNativeWidgetAura();
-  test::WidgetTest::WidgetAutoclosePtr bubble_widget(
-      BubbleDialogDelegateView::CreateBubble(bubble_delegate));
-  bubble_widget->SetFocusTraversableParent(
-      bubble_delegate->anchor_widget()->GetFocusTraversable());
-  bubble_widget->SetFocusTraversableParentView(parent2);
+
   View* child = new View();
   child->SetFocusBehavior(View::FocusBehavior::ALWAYS);
   bubble_widget->GetRootView()->AddChildView(child);
-  bubble_delegate->set_close_on_deactivate(false);
-  bubble_widget->Show();
 
   widget.Activate();
   parent1->RequestFocus();
@@ -1225,4 +1226,83 @@
   EXPECT_FALSE(left->HasFocus());
 }
 
+#if defined(USE_AURA)
+class RedirectToParentFocusManagerTest : public FocusManagerTest {
+ public:
+  RedirectToParentFocusManagerTest() = default;
+  RedirectToParentFocusManagerTest(const RedirectToParentFocusManagerTest&) =
+      delete;
+  RedirectToParentFocusManagerTest& operator=(
+      const RedirectToParentFocusManagerTest&) = delete;
+  ~RedirectToParentFocusManagerTest() override = default;
+
+  // FocusManagerTest:
+  void SetUp() override {
+    FocusManagerTest::SetUp();
+
+    View* anchor =
+        GetWidget()->GetRootView()->AddChildView(std::make_unique<View>());
+    anchor->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+
+    BubbleDialogDelegateView* bubble_delegate =
+        TestBubbleDialogDelegateView::CreateAndShowBubble(anchor);
+    Widget* bubble_widget = bubble_delegate->GetWidget();
+
+    parent_focus_manager_ = anchor->GetFocusManager();
+    bubble_focus_manager_ = bubble_widget->GetFocusManager();
+  }
+
+  void TearDown() override {
+    FocusManagerFactory::Install(nullptr);
+    FocusManagerTest::TearDown();
+  }
+
+ protected:
+  FocusManager* parent_focus_manager_;
+  FocusManager* bubble_focus_manager_;
+};
+
+// Test that when an accelerator is sent to a bubble that isn't registered,
+// the bubble's parent handles it instead.
+TEST_F(RedirectToParentFocusManagerTest, ParentHandlesAcceleratorFromBubble) {
+  ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
+  ui::TestAcceleratorTarget parent_return_target(true);
+
+  EXPECT_EQ(0, parent_return_target.accelerator_count());
+  parent_focus_manager_->RegisterAccelerator(
+      return_accelerator, ui::AcceleratorManager::kNormalPriority,
+      &parent_return_target);
+
+  EXPECT_TRUE(
+      !bubble_focus_manager_->IsAcceleratorRegistered(return_accelerator));
+  // Accelerator was proccesed by the parent.
+  EXPECT_TRUE(bubble_focus_manager_->ProcessAccelerator(return_accelerator));
+  EXPECT_EQ(parent_return_target.accelerator_count(), 1);
+}
+
+// Test that when an accelerator is sent to a bubble that is registered on both
+// it and its parent, the bubble handles it.
+TEST_F(RedirectToParentFocusManagerTest, BubbleHandlesRegisteredAccelerators) {
+  ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
+  ui::TestAcceleratorTarget parent_return_target(true);
+  ui::TestAcceleratorTarget bubble_return_target(true);
+
+  EXPECT_EQ(0, bubble_return_target.accelerator_count());
+  EXPECT_EQ(0, parent_return_target.accelerator_count());
+
+  bubble_focus_manager_->RegisterAccelerator(
+      return_accelerator, ui::AcceleratorManager::kNormalPriority,
+      &bubble_return_target);
+  parent_focus_manager_->RegisterAccelerator(
+      return_accelerator, ui::AcceleratorManager::kNormalPriority,
+      &parent_return_target);
+
+  // Accelerator was proccesed by the bubble and not by the parent.
+  EXPECT_TRUE(bubble_focus_manager_->ProcessAccelerator(return_accelerator));
+  EXPECT_EQ(1, bubble_return_target.accelerator_count());
+  EXPECT_EQ(0, parent_return_target.accelerator_count());
+}
+
+#endif
+
 }  // namespace views
diff --git a/ui/webui/webui_allowlist.cc b/ui/webui/webui_allowlist.cc
index 2232b83..62b5ca6 100644
--- a/ui/webui/webui_allowlist.cc
+++ b/ui/webui/webui_allowlist.cc
@@ -62,12 +62,20 @@
 void WebUIAllowlist::RegisterAutoGrantedPermission(const url::Origin& origin,
                                                    ContentSettingsType type,
                                                    ContentSetting setting) {
+  // It doesn't make sense to grant a default content setting.
+  DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
+
   // We only support auto-granting permissions to chrome://,
   // chrome-untrusted://, and devtools:// schemes.
   DCHECK(origin.scheme() == content::kChromeUIScheme ||
          origin.scheme() == content::kChromeUIUntrustedScheme ||
          origin.scheme() == content::kChromeDevToolsScheme);
 
+  // If the same permission is already registered, do nothing. We don't want to
+  // notify the provider of ContentSettingChange when it is unnecessary.
+  if (permissions_[type][origin] == setting)
+    return;
+
   permissions_[type][origin] = setting;
 
   // Notify the provider. |provider_| can be nullptr if
diff --git a/url/url_canon_fileurl.cc b/url/url_canon_fileurl.cc
index 6277289..067ed58 100644
--- a/url/url_canon_fileurl.cc
+++ b/url/url_canon_fileurl.cc
@@ -76,8 +76,8 @@
     Component sub_path = MakeRange(after_drive, path.end());
     Component fake_output_path;
     success = CanonicalizePath(spec, sub_path, output, &fake_output_path);
-  } else {
-    // No input path, canonicalize to a slash.
+  } else if (after_drive == path.begin) {
+    // No input path and no drive spec, canonicalize to a slash.
     output->push_back('/');
   }
 
diff --git a/url/url_canon_unittest.cc b/url/url_canon_unittest.cc
index b1b6dfd..87fb406 100644
--- a/url/url_canon_unittest.cc
+++ b/url/url_canon_unittest.cc
@@ -1517,6 +1517,10 @@
     {"file:///C:/gaba?query#ref", NULL, NULL, NULL, "filer", NULL, "/foo", "b", "c", "file://filer/foo?b#c"},
       // Replace nothing
     {"file:///C:/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///C:/gaba?query#ref"},
+    {"file:///Y:", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///Y:"},
+    {"file:///Y:/", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///Y:/"},
+    {"file:///./Y", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///Y"},
+    {"file:///./Y:", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///Y:"},
       // Clear non-path components (common)
     {"file:///C:/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, kDeleteComp, kDeleteComp, "file:///C:/gaba"},
       // Replace path with something that doesn't begin with a slash and make
@@ -1532,6 +1536,7 @@
 
   for (size_t i = 0; i < base::size(replace_cases); i++) {
     const ReplaceCase& cur = replace_cases[i];
+    SCOPED_TRACE(cur.base);
     int base_len = static_cast<int>(strlen(cur.base));
     Parsed parsed;
     ParseFileURL(cur.base, base_len, &parsed);
diff --git a/weblayer/browser/android/javatests/skew/expectations.txt b/weblayer/browser/android/javatests/skew/expectations.txt
index 0b3c487..6f1c3db 100644
--- a/weblayer/browser/android/javatests/skew/expectations.txt
+++ b/weblayer/browser/android/javatests/skew/expectations.txt
@@ -45,9 +45,10 @@
 # https://crbug.com/1079491.
 [ impl_lte_83 ] org.chromium.weblayer.test.NavigationTest#testSetUserAgentString [ Skip ]
 
+# Implemented in https://crrev.com/0193f6791133ee7a90d034aa4009
+[ impl_lte_81 ] org.chromium.weblayer.test.TabTest#testBeforeUnload [ Skip ]
+
 # Many M81 tests are broken, see https://crbug.com/1081102.
-[ impl_lte_81 ] org.chromium.weblayer.test.BottomControlsTest#testBasic [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.BottomControlsTest#testNoTopControl [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.BrowserFragmentLifecycleTest#restoreAfterRecreate [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.BrowserFragmentLifecycleTest#restoreTabGuidAfterRecreate [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.BrowserFragmentLifecycleTest#restoresTabGuid [ Skip ]
@@ -68,14 +69,12 @@
 [ impl_lte_81 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentInSameTabLaunchedOnLinkClick [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentWithFallbackUrlAfterRedirectLaunched [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.ExternalNavigationTest#testNonHandledExternalIntentWithFallbackUrlAfterRedirectGoesToFallbackUrl [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.FindInPageTest#testHideOnNavigate [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.InputTypesTest#testColorInput [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testRepostConfirmation [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testSetRequestHeaderInRedirect [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testSetRequestHeaderInStart [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testSetRequestHeaderThrowsExceptionInCompleted [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testSetRequestHeaderThrowsExceptionWithInvalidValue [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testSetUserAgentString [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testStopFromOnNavigationStarted [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.ProfileTest#testDestroyAndDeleteDataFromDisk [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.ProfileTest#testDestroyAndDeleteDataFromDiskIncognito [ Skip ]
@@ -87,13 +86,12 @@
 [ impl_lte_81 ] org.chromium.weblayer.test.TabCallbackTest#testShowContextMenuImg [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.TabCallbackTest#testTabModalOverlay [ Skip ]
 [ impl_lte_81 ] org.chromium.weblayer.test.TabCallbackTest#testTabModalOverlayOnBackgroundTab [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.TabTest#testBeforeUnload [ Skip ]
 
 # -------------------------------------
 # Tests against older WebLayer clients.
 # -------------------------------------
 
-# https://crbug.com/1078973.
+# Intentionally changed in M84, https://crrev.com/c/2130790. See https://crbug.com/1078973.
 [ client_lte_83 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentLaunchedViaOnLoad [ Skip ]
 [ client_lte_83 ] org.chromium.weblayer.test.ExternalNavigationTest#testNonHandledExternalIntentWithFallbackUrlThatLaunchesIntentAfterRedirectLaunchesFallbackIntent [ Skip ]
 
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java
index 31dbfd3..4f776eff 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java
@@ -5,7 +5,6 @@
 package org.chromium.weblayer_private;
 
 import android.app.Activity;
-import android.content.Context;
 import android.content.Intent;
 
 import org.chromium.base.ContextUtils;
@@ -26,6 +25,7 @@
     private boolean mTabDestroyed;
 
     public ExternalNavigationDelegateImpl(TabImpl tab) {
+        assert tab != null;
         mTab = tab;
     }
 
@@ -38,10 +38,6 @@
         return ContextUtils.activityFromContext(mTab.getBrowser().getContext());
     }
 
-    private Context getAvailableContext() {
-        return ExternalNavigationHandler.getAvailableContext(this);
-    }
-
     @Override
     public boolean willChromeHandleIntent(Intent intent) {
         return false;
@@ -163,13 +159,11 @@
 
     @Override
     public WindowAndroid getWindowAndroid() {
-        if (mTab == null) return null;
         return mTab.getBrowser().getWindowAndroid();
     }
 
     @Override
     public WebContents getWebContents() {
-        if (mTab == null) return null;
         return mTab.getWebContents();
     }
 
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
index 10c193d..b17e3ab 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -40,6 +40,7 @@
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.components.embedder_support.application.ClassLoaderContextWrapperFactory;
+import org.chromium.components.embedder_support.application.FirebaseConfig;
 import org.chromium.content_public.browser.BrowserStartupController;
 import org.chromium.content_public.browser.ChildProcessCreationParams;
 import org.chromium.content_public.browser.DeviceUtils;
@@ -214,6 +215,8 @@
         }
 
         BuildInfo.setBrowserPackageInfo(packageInfo);
+        BuildInfo.setFirebaseAppId(
+                FirebaseConfig.getFirebaseAppIdForPackage(packageInfo.packageName));
         // TODO: The call to onResourcesLoaded() can be slow, we may need to parallelize this with
         // other expensive startup tasks.
         R.onResourcesLoaded(forceCorrectPackageId(remoteContext));