diff --git a/.vpython b/.vpython
index b67113e5..4332083e 100644
--- a/.vpython
+++ b/.vpython
@@ -307,7 +307,7 @@
 >
 wheel: <
   name: "infra/python/wheels/mozlog-py2_py3"
-  version: "version:4.2.0"
+  version: "version:5.0"
 >
 wheel: <
   name: "infra/python/wheels/mozprocess-py2_py3"
diff --git a/BUILD.gn b/BUILD.gn
index d2c9c44..670ea64 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -85,6 +85,7 @@
     "//tools/ipc_fuzzer:ipc_fuzzer_all",
     "//tools/metrics:metrics_metadata",
     "//ui/base:ui_base_unittests",
+    "//ui/color:color_unittests",
     "//ui/gfx:gfx_unittests",
     "//url:url_unittests",
   ]
@@ -829,10 +830,6 @@
     ]
   }
 
-  if (use_color_pipeline) {
-    deps += [ "//ui/color:color_unittests" ]
-  }
-
   # PFFFT.
   deps += [
     "//third_party/pffft:fuzzers",
diff --git a/DEPS b/DEPS
index 56c4cf97..d2c09ce 100644
--- a/DEPS
+++ b/DEPS
@@ -175,11 +175,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': 'c6d0fdfb40987231be61ba3a47f6b34013a04606',
+  'skia_revision': 'eced98b5eacf5d2cf4368c26020c0e3032c963f6',
   # 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': 'e18618490c3aeddf96e36c11b4bffcb5c7f12320',
+  'v8_revision': 'ddac4370235572a0d7c8dcf6ef65d1933104c078',
   # 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.
@@ -187,11 +187,11 @@
   # 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': '3f283eb209adf9405721a05e8323bd80069912ad',
+  'angle_revision': '27a813089d835c1ad706237c7adf70f5e0e1f59f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '126720bd2e578fec077a57d877ac64c46b18cd52',
+  'swiftshader_revision': '2e6cd9c400d193592e3a1f17aa200e50c152e5d9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -246,7 +246,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '07fd18460d096895e12a7f8b228cf9d2fbf117d6',
+  'devtools_frontend_revision': '4eb885a01e2cb08f764e68bd71dec0663504370e',
   # 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.
@@ -863,7 +863,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '181bb9733f8392b5dbea5fe37545f29251268ea6',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8e76ccdb37b004f5a5eaa13e398a9f318c92b9c6',
       'condition': 'checkout_linux',
   },
 
@@ -1239,7 +1239,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '0fd4f5cda74d29ee13a8942f973c827ec73d7b0d',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'fb875c61cf00ded88a3f46cb562ab943129cb7af',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1440,7 +1440,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'ec18cc3262922e7dcdbe70243c6f40606f979144',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '4518a20e14ecb3415c52aecf132d9bc83edaa9c4',
+    Var('webrtc_git') + '/src.git' + '@' + '589b41e743db232fabde3f24653c76db45d6cedf',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1515,7 +1515,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3ac359c9ea04878a029df2e976aa4bcf11a273fc',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@040b65bece28b2adc73f3b8cab7e2586dbada447',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/common/crash_reporter/crash_keys.cc b/android_webview/common/crash_reporter/crash_keys.cc
index 7898c363..9be81b1 100644
--- a/android_webview/common/crash_reporter/crash_keys.cc
+++ b/android_webview/common/crash_reporter/crash_keys.cc
@@ -51,6 +51,8 @@
     "mojo-message-error__3",
     "mojo-message-error__4",
     "total-discardable-memory-allocated",
+    // TODO(https://crbug.com/1006814): Remove this.
+    "IsRenderFrameLive",
 
     // GWP-ASan
     gwp_asan::kMallocCrashKey,
diff --git a/ash/shelf/shelf_navigation_widget.cc b/ash/shelf/shelf_navigation_widget.cc
index 93308236..c50a85f 100644
--- a/ash/shelf/shelf_navigation_widget.cc
+++ b/ash/shelf/shelf_navigation_widget.cc
@@ -400,7 +400,9 @@
                                              ShelfView* shelf_view)
     : shelf_(shelf),
       delegate_(new ShelfNavigationWidget::Delegate(shelf, shelf_view)),
-      bounds_animator_(std::make_unique<views::BoundsAnimator>(delegate_)),
+      bounds_animator_(
+          std::make_unique<views::BoundsAnimator>(delegate_,
+                                                  /*use_transforms=*/true)),
       back_button_metrics_reporter_(
           std::make_unique<NavigationButtonAnimationMetricsReporter>(
               shelf,
diff --git a/ash/wm/gestures/back_gesture/back_gesture_contextual_nudge.cc b/ash/wm/gestures/back_gesture/back_gesture_contextual_nudge.cc
index dcd6dd0..9555b15 100644
--- a/ash/wm/gestures/back_gesture/back_gesture_contextual_nudge.cc
+++ b/ash/wm/gestures/back_gesture/back_gesture_contextual_nudge.cc
@@ -23,7 +23,7 @@
 namespace {
 
 // Width of the contextual nudge.
-constexpr int kBackgroundWidth = 160;
+constexpr int kBackgroundWidth = 320;
 
 // Colors of the contextual nudge background gradient.
 constexpr SkColor kBackgroundStartColor =
diff --git a/build/android/gyp/java_cpp_enum.py b/build/android/gyp/java_cpp_enum.py
index 502e071..d787809e 100755
--- a/build/android/gyp/java_cpp_enum.py
+++ b/build/android/gyp/java_cpp_enum.py
@@ -69,7 +69,7 @@
     # Enums, if given no value, are given the value of the previous enum + 1.
     if not all(self.entries.values()):
       prev_enum_value = -1
-      for key, value in self.entries.iteritems():
+      for key, value in self.entries.items():
         if not value:
           self.entries[key] = prev_enum_value + 1
         elif value in self.entries:
@@ -102,9 +102,9 @@
 
     def StripEntries(entries):
       ret = collections.OrderedDict()
-      for k, v in entries.iteritems():
+      for k, v in entries.items():
         stripped_key = k.replace(prefix_to_strip, '', 1)
-        if isinstance(v, basestring):
+        if isinstance(v, str):
           stripped_value = v.replace(prefix_to_strip, '')
         else:
           stripped_value = v
@@ -124,10 +124,10 @@
   """Normalize keys in |d| and update references to old keys in |d| values."""
   normal_keys = {k: func(k) for k in d}
   ret = collections.OrderedDict()
-  for k, v in d.iteritems():
+  for k, v in d.items():
     # Need to transform values as well when the entry value was explicitly set
     # (since it could contain references to other enum entry values).
-    if isinstance(v, basestring):
+    if isinstance(v, str):
       for normal_key in normal_keys:
         v = v.replace(normal_key, normal_keys[normal_key])
     ret[normal_keys[k]] = v
@@ -368,7 +368,7 @@
   enum_template = Template('  int ${NAME} = ${VALUE};')
   enum_entries_string = []
   enum_names = []
-  for enum_name, enum_value in enum_definition.entries.iteritems():
+  for enum_name, enum_value in enum_definition.entries.items():
     values = {
         'NAME': enum_name,
         'VALUE': enum_value,
diff --git a/build/android/gyp/java_cpp_enum_tests.py b/build/android/gyp/java_cpp_enum_tests.py
index 08ef3b87..088c450 100755
--- a/build/android/gyp/java_cpp_enum_tests.py
+++ b/build/android/gyp/java_cpp_enum_tests.py
@@ -730,7 +730,7 @@
     definition.AppendEntry('B', None)
     definition.AppendEntry('NAME_LAST', None)
     definition.Finalize()
-    self.assertEqual(['A', 'B', 'NAME_LAST'], definition.entries.keys())
+    self.assertEqual(['A', 'B', 'NAME_LAST'], list(definition.entries.keys()))
 
   def testGenerateThrowsOnEmptyInput(self):
     with self.assertRaises(Exception):
diff --git a/cc/OWNERS b/cc/OWNERS
index 3ef17768..682d6e8 100644
--- a/cc/OWNERS
+++ b/cc/OWNERS
@@ -44,6 +44,7 @@
 # surfaces
 kylechar@chromium.org
 samans@chromium.org
+jonross@chromium.org
 
 # input, scrolling
 bokan@chromium.org
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 584aff4..11e1b3d 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -1111,7 +1111,7 @@
     scoped_refptr<Layer> root_layer) {
   SetRootLayer(std::move(root_layer));
 
-  DCHECK(root_layer_->children().empty());
+  DCHECK(!root_layer || root_layer_->children().empty());
   if (IsUsingLayerLists() && root_layer_)
     force_use_property_tree_builder_ = true;
 }
diff --git a/cc/trees/layer_tree_host_pixeltest_readback.cc b/cc/trees/layer_tree_host_pixeltest_readback.cc
index 7e7e7a7..d314bdd 100644
--- a/cc/trees/layer_tree_host_pixeltest_readback.cc
+++ b/cc/trees/layer_tree_host_pixeltest_readback.cc
@@ -421,12 +421,11 @@
 #if !defined(GL_NOT_ON_PLATFORM)
     ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_TEXTURE},
     ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_BITMAP},
-#if !defined(OS_WIN) && !defined(OS_LINUX)
     // TODO(crbug.com/1046788): The skia readback path doesn't support
     // RGBA_TEXTURE readback requests yet. Don't run these tests on platforms
     // that have UseSkiaForGLReadback enabled by default.
-    ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE},
-#endif
+    //
+    // ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE},
     ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_BITMAP},
 #endif  // !defined(GL_NOT_ON_PLATFORM)
 #if defined(ENABLE_CC_VULKAN_TESTS)
@@ -445,12 +444,11 @@
 #if !defined(GL_NOT_ON_PLATFORM)
     ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_TEXTURE},
     ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_BITMAP},
-#if !defined(OS_WIN) && !defined(OS_LINUX)
     // TODO(crbug.com/1046788): The skia readback path doesn't support
     // RGBA_TEXTURE readback requests yet. Don't run these tests on platforms
     // that have UseSkiaForGLReadback enabled by default.
-    ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE},
-#endif
+    //
+    // ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE},
     ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_BITMAP},
 #endif  // !defined(GL_NOT_ON_PLATFORM)
 #if defined(ENABLE_CC_VULKAN_TESTS) && !defined(THREAD_SANITIZER) && \
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 3ae509b..a1763a2 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1383,12 +1383,12 @@
   "java/src/org/chromium/chrome/browser/settings/homepage/HomepageEditor.java",
   "java/src/org/chromium/chrome/browser/settings/homepage/HomepageSettings.java",
   "java/src/org/chromium/chrome/browser/settings/homepage/RadioButtonGroupHomepagePreference.java",
-  "java/src/org/chromium/chrome/browser/settings/languages/AddLanguageFragment.java",
-  "java/src/org/chromium/chrome/browser/settings/languages/LanguageItem.java",
-  "java/src/org/chromium/chrome/browser/settings/languages/LanguageListBaseAdapter.java",
-  "java/src/org/chromium/chrome/browser/settings/languages/LanguageListPreference.java",
-  "java/src/org/chromium/chrome/browser/settings/languages/LanguageSettings.java",
-  "java/src/org/chromium/chrome/browser/settings/languages/LanguagesManager.java",
+  "java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java",
+  "java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java",
+  "java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java",
+  "java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java",
+  "java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java",
+  "java/src/org/chromium/chrome/browser/language/settings/LanguagesManager.java",
   "java/src/org/chromium/chrome/browser/settings/password/CallbackDelayer.java",
   "java/src/org/chromium/chrome/browser/settings/password/DialogManager.java",
   "java/src/org/chromium/chrome/browser/settings/password/ExportErrorDialogFragment.java",
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamItemTouchCallbacks.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamItemTouchCallbacks.java
index 8076c69..f8395d7 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamItemTouchCallbacks.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/library/basicstream/internal/StreamItemTouchCallbacks.java
@@ -5,12 +5,13 @@
 package org.chromium.chrome.browser.feed.library.basicstream.internal;
 
 import android.graphics.Canvas;
-import android.support.v4.view.animation.FastOutLinearInInterpolator;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.support.v7.widget.helper.ItemTouchHelper;
 import android.view.animation.Interpolator;
 
+import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
+
 import org.chromium.chrome.browser.feed.library.basicstream.internal.viewholders.SwipeableViewHolder;
 
 /** {@link ItemTouchHelper.Callback} to allow dismissing cards via swiping. */
diff --git a/chrome/android/java/res/xml/languages_preferences.xml b/chrome/android/java/res/xml/languages_preferences.xml
index 5e7dfc6..550563e 100644
--- a/chrome/android/java/res/xml/languages_preferences.xml
+++ b/chrome/android/java/res/xml/languages_preferences.xml
@@ -6,7 +6,7 @@
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <org.chromium.chrome.browser.settings.languages.LanguageListPreference
+    <org.chromium.chrome.browser.language.settings.LanguageListPreference
         android:key="preferred_languages"
         android:layout="@layout/languages_preference"
         android:widgetLayout="@layout/accept_languages_list" />
diff --git a/chrome/android/java/res/xml/main_preferences.xml b/chrome/android/java/res/xml/main_preferences.xml
index f5cccd3c..370468e 100644
--- a/chrome/android/java/res/xml/main_preferences.xml
+++ b/chrome/android/java/res/xml/main_preferences.xml
@@ -81,7 +81,7 @@
         android:order="14"
         android:title="@string/prefs_site_settings"/>
     <Preference
-        android:fragment="org.chromium.chrome.browser.settings.languages.LanguageSettings"
+        android:fragment="org.chromium.chrome.browser.language.settings.LanguageSettings"
         android:key="languages"
         android:order="15"
         android:title="@string/language_settings"/>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
index 56a1b97..a107475 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
@@ -56,7 +56,6 @@
 import org.chromium.chrome.browser.xsurface.SurfaceDependencyProvider;
 import org.chromium.chrome.browser.xsurface.SurfaceRenderer;
 import org.chromium.components.browser_ui.widget.FeatureHighlightProvider;
-import org.chromium.components.download.DownloadCollectionBridge;
 import org.chromium.components.signin.AccountManagerDelegate;
 import org.chromium.components.signin.SystemAccountManagerDelegate;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
@@ -330,13 +329,6 @@
     }
 
     /**
-     * @return A new {@link DownloadCollectionBridge} instance.
-     */
-    public DownloadCollectionBridge getDownloadCollectionBridge() {
-        return DownloadCollectionBridge.getDownloadCollectionBridge();
-    }
-
-    /**
      * @return A new {@link DigitalWellbeingClient} instance.
      */
     public DigitalWellbeingClient createDigitalWellbeingClient() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/TabbedModeTabDelegateFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/TabbedModeTabDelegateFactory.java
index a00cbd2f..ba56290 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/TabbedModeTabDelegateFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/TabbedModeTabDelegateFactory.java
@@ -8,6 +8,7 @@
 import org.chromium.chrome.browser.contextmenu.ChromeContextMenuPopulator;
 import org.chromium.chrome.browser.contextmenu.ContextMenuPopulator;
 import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
+import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler;
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.tab.Tab;
@@ -43,7 +44,7 @@
 
     @Override
     public ExternalNavigationHandler createExternalNavigationHandler(Tab tab) {
-        return new ExternalNavigationHandler(tab);
+        return new ExternalNavigationHandler(new ExternalNavigationDelegateImpl(tab));
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
index 164b30e3..7f2eae4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
@@ -19,6 +19,7 @@
 import org.chromium.chrome.browser.WebContentsFactory;
 import org.chromium.chrome.browser.content.ContentUtils;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
+import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.embedder_support.delegate.WebContentsDelegateAndroid;
@@ -142,7 +143,7 @@
         public InterceptNavigationDelegateImpl() {
             Tab tab = mActivity.getActivityTab();
             mExternalNavHandler = (tab != null && tab.getWebContents() != null)
-                    ? new ExternalNavigationHandler(tab)
+                    ? new ExternalNavigationHandler(new ExternalNavigationDelegateImpl(tab))
                     : null;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
index 04d22c2..304138a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
@@ -23,9 +23,10 @@
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.signin.DisplayableProfileData;
+import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.ProfileDataCache;
 import org.chromium.components.signin.AccountManagerFacade;
-import org.chromium.components.signin.ChromeSigninController;
+import org.chromium.components.signin.base.CoreAccountInfo;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -438,10 +439,10 @@
     private String getOwnerEmail() {
         if (sTestOwnerEmail != null) return sTestOwnerEmail;
 
-        ChromeSigninController controller = ChromeSigninController.get();
-        Account account = controller.getSignedInUser();
-        if (account != null) {
-            return account.name;
+        CoreAccountInfo coreAccountInfo =
+                IdentityServicesProvider.get().getIdentityManager().getPrimaryAccountInfo();
+        if (coreAccountInfo != null) {
+            return coreAccountInfo.getEmail();
         }
 
         AccountManagerFacade manager = AccountManagerFacade.get();
@@ -504,8 +505,8 @@
         DisplayableProfileData profileData = mProfileDataCache.getProfileDataOrDefault(ownerEmail);
         String name = profileData.getFullNameOrEmail();
         if (TextUtils.isEmpty(name) || TextUtils.equals(name, ownerEmail)) {
-            ChromeSigninController controller = ChromeSigninController.get();
-            name = controller.getSignedInAccountName();
+            name = CoreAccountInfo.getEmailFrom(
+                    IdentityServicesProvider.get().getIdentityManager().getPrimaryAccountInfo());
         }
 
         ArrayList<String> telephones = new ArrayList<>();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index d0a11e14..9d363dda 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -50,6 +50,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.util.ConversionUtils;
+import org.chromium.components.download.DownloadCollectionBridge;
 import org.chromium.components.download.DownloadState;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.Tracker;
@@ -278,6 +279,7 @@
         mHandler = handler;
         mDownloadSnackbarController = new DownloadSnackbarController();
         mOMADownloadHandler = new OMADownloadHandler(applicationContext);
+        DownloadCollectionBridge.setDownloadDelegate(new DownloadDelegateImpl());
         // Note that this technically leaks the native object, however, DownloadManagerService
         // is a singleton that lives forever and there's no clean shutdown of Chrome on Android.
         init();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegate.java
index 4ed2168..a743d93 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegate.java
@@ -160,4 +160,10 @@
      * @return Whether the package is a valid WebAPK package.
      */
     boolean isValidWebApk(String packageName);
+
+    /**
+     * Gives the embedder a chance to handle the intent via the autofill assistant.
+     */
+    boolean handleWithAutofillAssistant(
+            ExternalNavigationParams params, Intent targetIntent, String browserFallbackUrl);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
index f86115d..8a18d1d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
@@ -39,12 +39,14 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity2;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.LaunchIntentDispatcher;
+import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantFacade;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler.OverrideUrlLoadingResult;
 import org.chromium.chrome.browser.instantapps.AuthenticatedProxyActivity;
 import org.chromium.chrome.browser.instantapps.InstantAppsHandler;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tab.TabRedirectHandler;
 import org.chromium.chrome.browser.webapps.WebappActivity;
@@ -644,4 +646,20 @@
     public boolean isValidWebApk(String packageName) {
         return WebApkValidator.isValidWebApk(ContextUtils.getApplicationContext(), packageName);
     }
+
+    @Override
+    public boolean handleWithAutofillAssistant(
+            ExternalNavigationParams params, Intent targetIntent, String browserFallbackUrl) {
+        if (browserFallbackUrl != null && !params.isIncognito()
+                && AutofillAssistantFacade.isAutofillAssistantByIntentTriggeringEnabled(
+                        targetIntent)
+                && isSerpReferrer()) {
+            if (params.getTab() != null) {
+                AutofillAssistantFacade.start(((TabImpl) params.getTab()).getActivity(),
+                        targetIntent.getExtras(), browserFallbackUrl);
+            }
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
index 01ec93b..7dce21e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
@@ -27,11 +27,8 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.IntentHandler;
-import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantFacade;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.instantapps.InstantAppsHandler;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabRedirectHandler;
 import org.chromium.chrome.browser.webapps.WebappScopePolicy;
 import org.chromium.components.embedder_support.util.UrlConstants;
@@ -154,15 +151,6 @@
     }
 
     /**
-     * A constructor for UrlHandler.
-     *
-     * @param tab The tab that initiated the external intent.
-     */
-    public ExternalNavigationHandler(Tab tab) {
-        this(new ExternalNavigationDelegateImpl(tab));
-    }
-
-    /**
      * Constructs a new instance of {@link ExternalNavigationHandler}, using the injected
      * {@link ExternalNavigationDelegate}.
      */
@@ -765,14 +753,7 @@
 
     private boolean handleWithAutofillAssistant(
             ExternalNavigationParams params, Intent targetIntent, String browserFallbackUrl) {
-        if (browserFallbackUrl != null && !params.isIncognito()
-                && AutofillAssistantFacade.isAutofillAssistantByIntentTriggeringEnabled(
-                        targetIntent)
-                && mDelegate.isSerpReferrer()) {
-            if (params.getTab() != null) {
-                AutofillAssistantFacade.start(((TabImpl) params.getTab()).getActivity(),
-                        targetIntent.getExtras(), browserFallbackUrl);
-            }
+        if (mDelegate.handleWithAutofillAssistant(params, targetIntent, browserFallbackUrl)) {
             if (DEBUG) Log.i(TAG, "Handling with Assistant");
             return true;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
index 7d3e6d4..70d2614 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
@@ -85,8 +85,6 @@
 
     private static FirstRunActivityObserver sObserver;
 
-    private boolean mShowWelcomePage = true;
-
     private String mResultSignInAccountName;
     private boolean mResultIsDefaultAccount;
     private boolean mResultShowSignInSettings;
@@ -121,12 +119,8 @@
      * Defines a sequence of pages to be shown (depending on parameters etc).
      */
     private void createPageSequence() {
-        // An optional welcome page.
-        if (mShowWelcomePage) {
-            mPages.add(new ToSAndUMAFirstRunFragment.Page());
-            mFreProgressStates.add(FRE_PROGRESS_WELCOME_SHOWN);
-        }
-
+        mPages.add(new ToSAndUMAFirstRunFragment.Page());
+        mFreProgressStates.add(FRE_PROGRESS_WELCOME_SHOWN);
         // Other pages will be created by createPostNativePageSequence() after
         // native has been initialized.
     }
@@ -207,7 +201,6 @@
                 }
 
                 mFreProperties = freProperties;
-                mShowWelcomePage = mFreProperties.getBoolean(SHOW_WELCOME_PAGE);
                 if (TextUtils.isEmpty(mResultSignInAccountName)) {
                     mResultSignInAccountName = mFreProperties.getString(
                             SigninFirstRunFragment.FORCE_SIGNIN_ACCOUNT_TO);
@@ -464,7 +457,7 @@
     private boolean jumpToPage(int position) {
         if (sObserver != null) sObserver.onJumpToPage(position);
 
-        if (mShowWelcomePage && !didAcceptTermsOfService()) {
+        if (!didAcceptTermsOfService()) {
             return position == 0;
         }
         if (position >= mPagerAdapter.getCount()) {
@@ -478,7 +471,7 @@
 
     private void stopProgressionIfNotAcceptedTermsOfService() {
         if (mPagerAdapter == null) return;
-        mPagerAdapter.setStopAtTheFirstPage(mShowWelcomePage && !didAcceptTermsOfService());
+        mPagerAdapter.setStopAtTheFirstPage(!didAcceptTermsOfService());
     }
 
     private void skipPagesIfNecessary() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivityBase.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivityBase.java
index f4f70c1..66b90418 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivityBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivityBase.java
@@ -32,8 +32,6 @@
     // received by ChromeLauncherActivity.)
     public static final String EXTRA_CHROME_LAUNCH_INTENT_EXTRAS =
             "Extra.FreChromeLaunchIntentExtras";
-
-    static final String SHOW_WELCOME_PAGE = "ShowWelcome";
     static final String SHOW_DATA_REDUCTION_PAGE = "ShowDataReduction";
     static final String SHOW_SEARCH_ENGINE_PAGE = "ShowSearchEnginePage";
     static final String SHOW_SIGNIN_PAGE = "ShowSignIn";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
index 5b48db0..2fe5f65 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
@@ -162,8 +162,6 @@
         }
 
         Bundle freProperties = new Bundle();
-        // TODO(https://crbug.com/1056132): Remove the boolean FirstRunActivity.SHOW_WELCOME_PAGE
-        freProperties.putBoolean(FirstRunActivity.SHOW_WELCOME_PAGE, true);
         freProperties.putInt(SigninFirstRunFragment.CHILD_ACCOUNT_STATUS, mChildAccountStatus);
 
         // Initialize usage and crash reporting according to the default value.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index 838b72e..e5794b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -116,6 +116,7 @@
 
     private static Map<String, Boolean> sBoolValuesReturned = new HashMap<>();
     private static Map<String, String> sStringValuesReturned = new HashMap<>();
+    private static Map<String, Integer> sIntValuesReturned = new HashMap<>();
     private static String sReachedCodeProfilerTrialGroup;
 
     /**
@@ -262,6 +263,17 @@
                 parameter.getSharedPreferenceKey(), parameter.getDefaultValue());
     }
 
+    /**
+     * TODO(crbug.com/1012975): Move this to IntCachedFieldTrialParameter when
+     * CachedFeatureFlags is in chrome/browser/flags.
+     *
+     * @return the value of the field trial parameter that should be used in this run.
+     */
+    public static int getValue(IntCachedFieldTrialParameter parameter) {
+        return getConsistentIntValue(
+                parameter.getSharedPreferenceKey(), parameter.getDefaultValue());
+    }
+
     private static void cacheStartSurfaceVariation() {
         String feature = ChromeFeatureList.getFieldTrialParamByFeature(
                 ChromeFeatureList.START_SURFACE_ANDROID, "start_surface_variation");
@@ -358,6 +370,15 @@
         return value;
     }
 
+    static int getConsistentIntValue(String preferenceName, int defaultValue) {
+        Integer value = sIntValuesReturned.get(preferenceName);
+        if (value == null) {
+            value = SharedPreferencesManager.getInstance().readInt(preferenceName, defaultValue);
+            sIntValuesReturned.put(preferenceName, value);
+        }
+        return value;
+    }
+
     private static String getPrefForFeatureFlag(String featureName) {
         String grandfatheredPrefKey = sNonDynamicPrefKeys.get(featureName);
         if (grandfatheredPrefKey == null) {
@@ -371,6 +392,7 @@
     public static void resetFlagsForTesting() {
         sBoolValuesReturned.clear();
         sStringValuesReturned.clear();
+        sIntValuesReturned.clear();
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
index e0dbe65..2c5e6cc7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
@@ -16,7 +16,6 @@
 import android.view.View;
 import android.webkit.MimeTypeMap;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.task.AsyncTask;
@@ -84,11 +83,8 @@
                 new AsyncTask<String>() {
                     @Override
                     protected String doInBackground() {
-                        if (BuildInfo.isAtLeastQ()
-                                && DownloadCollectionBridge.getDownloadCollectionBridge()
-                                           .needToPublishDownload(mFilePath)) {
-                            Uri uri = DownloadCollectionBridge.getDownloadCollectionBridge()
-                                              .getDownloadUriForFileName(filename);
+                        if (DownloadCollectionBridge.shouldPublishDownload(mFilePath)) {
+                            Uri uri = DownloadCollectionBridge.getDownloadUriForFileName(filename);
                             return uri == null ? null : uri.toString();
                         } else {
                             if (file.exists()) return mFilePath;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
index 9970f86..2581366 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -81,7 +81,6 @@
 import org.chromium.chrome.browser.webapps.WebApkVersionManager;
 import org.chromium.chrome.browser.webapps.WebappRegistry;
 import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
-import org.chromium.components.download.DownloadCollectionBridge;
 import org.chromium.components.minidump_uploader.CrashFileManager;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountsChangeObserver;
@@ -185,11 +184,6 @@
                         application, ChromePreferenceKeys.SYNC_SESSIONS_UUID),
                 false);
 
-        // Set up the DownloadCollectionBridge early as display names may be immediately retrieved
-        // after native is loaded.
-        DownloadCollectionBridge.setDownloadCollectionBridge(
-                AppHooks.get().getDownloadCollectionBridge());
-
         // De-jelly can also be controlled by a system property. As sandboxed processes can't
         // read this property directly, convert it to the equivalent command line flag.
         if (DeJellyUtils.externallyEnableDeJelly()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java
index 1b4b32b..2ef6f00 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java
@@ -22,7 +22,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.settings.languages.LanguageItem;
+import org.chromium.chrome.browser.language.settings.LanguageItem;
 import org.chromium.chrome.browser.translate.TranslateBridge;
 import org.chromium.components.language.AndroidLanguageMetricsBridge;
 import org.chromium.components.language.GeoLanguageProviderBridge;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/language/OWNERS
new file mode 100644
index 0000000..f1e82d5
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/language/OWNERS
@@ -0,0 +1 @@
+file://components/language/OWNERS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/AddLanguageFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/languages/AddLanguageFragment.java
rename to chrome/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java
index 4f2490b..335168b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/AddLanguageFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java
@@ -2,7 +2,7 @@
 // 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.settings.languages;
+package org.chromium.chrome.browser.language.settings;
 
 import android.app.Activity;
 import android.content.Context;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageItem.java b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageItem.java
rename to chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java
index b1c58fe..511465a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java
@@ -2,7 +2,7 @@
 // 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.settings.languages;
+package org.chromium.chrome.browser.language.settings;
 
 /**
  * Simple object representing the language item.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageListBaseAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageListBaseAdapter.java
rename to chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java
index 70cf5b5..ac858350 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageListBaseAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java
@@ -2,7 +2,7 @@
 // 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.settings.languages;
+package org.chromium.chrome.browser.language.settings;
 
 import android.content.Context;
 import android.support.v4.view.ViewCompat;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageListPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java
similarity index 99%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageListPreference.java
rename to chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java
index c647a0a..11eb1896 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageListPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java
@@ -2,7 +2,7 @@
 // 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.settings.languages;
+package org.chromium.chrome.browser.language.settings;
 
 import static org.chromium.components.browser_ui.widget.listmenu.BasicListMenu.buildMenuListItem;
 import static org.chromium.components.browser_ui.widget.listmenu.BasicListMenu.buildMenuListItemWithEndIcon;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageSettings.java
rename to chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java
index 2152e65..5b9276f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguageSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java
@@ -2,7 +2,7 @@
 // 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.settings.languages;
+package org.chromium.chrome.browser.language.settings;
 
 import android.app.Activity;
 import android.content.Intent;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguagesManager.java b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguagesManager.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguagesManager.java
rename to chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguagesManager.java
index 61a484f2..46a8a49 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/languages/LanguagesManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguagesManager.java
@@ -2,7 +2,7 @@
 // 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.settings.languages;
+package org.chromium.chrome.browser.language.settings;
 
 import androidx.annotation.IntDef;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/permissions/AndroidPermissionRequester.java b/chrome/android/java/src/org/chromium/chrome/browser/permissions/AndroidPermissionRequester.java
index 4d465f0..b64aacd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/permissions/AndroidPermissionRequester.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/permissions/AndroidPermissionRequester.java
@@ -139,6 +139,10 @@
                         } else if (deniedContentSettings.contains(
                                            ContentSettingsType.MEDIASTREAM_CAMERA)) {
                             deniedStringId = R.string.infobar_missing_camera_permission_text;
+                        } else if (deniedContentSettings.contains(ContentSettingsType.AR)) {
+                            // TODO(https://crbug.com/1058055): Use the missing camera permission
+                            // text until we get guidance from UX team.
+                            deniedStringId = R.string.infobar_missing_camera_permission_text;
                         }
                     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettingsCategory.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettingsCategory.java
index da0e67e..a03d640 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettingsCategory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/website/SiteSettingsCategory.java
@@ -445,10 +445,14 @@
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_SITE_SETTINGS_UI_REFRESH)) {
             if (type == ContentSettingsType.GEOLOCATION) {
                 permission_string = R.string.android_location_permission_off;
-            } else if (type == ContentSettingsType.MEDIASTREAM_CAMERA) {
-                permission_string = R.string.android_camera_permission_off;
             } else if (type == ContentSettingsType.MEDIASTREAM_MIC) {
                 permission_string = R.string.android_microphone_permission_off;
+            } else if (type == ContentSettingsType.MEDIASTREAM_CAMERA) {
+                permission_string = R.string.android_camera_permission_off;
+            } else if (type == ContentSettingsType.AR) {
+                // TODO(https://crbug.com/1058055): Use the missing camera permission
+                // text until we get guidance from UX team.
+                permission_string = R.string.android_camera_permission_off;
             } else if (type == ContentSettingsType.NOTIFICATIONS) {
                 permission_string = R.string.android_notifications_permission_off;
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarAnimationCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarAnimationCoordinator.java
index 045c79bd3..82fdd94 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarAnimationCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarAnimationCoordinator.java
@@ -9,11 +9,12 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.res.Resources;
-import android.support.v4.view.animation.FastOutLinearInInterpolator;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
 
+import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
+import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
+
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
 import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/translate/TranslateBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/translate/TranslateBridge.java
index e1a8ba22..b09bd99c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/translate/TranslateBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/translate/TranslateBridge.java
@@ -6,7 +6,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.chrome.browser.settings.languages.LanguageItem;
+import org.chromium.chrome.browser.language.settings.LanguageItem;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.browser.WebContents;
 
diff --git a/chrome/android/java/static_library_dex_reference_workarounds.flags b/chrome/android/java/static_library_dex_reference_workarounds.flags
index 3765231..9367e59 100644
--- a/chrome/android/java/static_library_dex_reference_workarounds.flags
+++ b/chrome/android/java/static_library_dex_reference_workarounds.flags
@@ -34,6 +34,10 @@
 -keep,allowobfuscation class org.chromium.chrome.browser.firstrun.FirstRunPagerAdapter { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.init.ChromeBrowserInitializer$** { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.init.ProcessInitializationHandler$** { *; }
+-keep,allowobfuscation class org.chromium.chrome.browser.language.settings.AddLanguageFragment { *; }
+-keep,allowobfuscation class org.chromium.chrome.browser.language.settings.AddLanguageFragment$$Lambda$1 { *; }
+-keep,allowobfuscation class org.chromium.chrome.browser.language.settings.AddLanguageFragment$LanguageSearchListAdapter { *; }
+-keep,allowobfuscation class org.chromium.chrome.browser.language.settings.LanguageListBaseAdapter { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.media.ui.MediaImageCallback { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.media.ui.MediaImageManager { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.media.ui.MediaNotificationInfo$Builder { *; }
@@ -44,8 +48,6 @@
 -keep,allowobfuscation class org.chromium.chrome.browser.payments.AndroidPaymentApp$** { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.payments.PaymentApp { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.photo_picker.PhotoPickerDialog { *; }
--keep,allowobfuscation class org.chromium.chrome.browser.preferences.languages.AddLanguageFragment { *; }
--keep,allowobfuscation class org.chromium.chrome.browser.preferences.languages.AddLanguageFragment$$Lambda$** { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.preferences.privacy.ClearBrowsingDataFetcher { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.preferences.privacy.ClearBrowsingDataPreferences { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.preferences.privacy.ClearBrowsingDataPreferencesAdvanced { *; }
@@ -53,10 +55,6 @@
 -keep,allowobfuscation class org.chromium.chrome.browser.preferences.privacy.ClearBrowsingDataTabsFragment { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.preferences.privacy.ClearBrowsingDataTabsFragment$ClearBrowsingDataPagerAdapter { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.profiles.Profile { *; }
--keep,allowobfuscation class org.chromium.chrome.browser.settings.languages.AddLanguageFragment { *; }
--keep,allowobfuscation class org.chromium.chrome.browser.settings.languages.AddLanguageFragment$$Lambda$1 { *; }
--keep,allowobfuscation class org.chromium.chrome.browser.settings.languages.AddLanguageFragment$LanguageSearchListAdapter { *; }
--keep,allowobfuscation class org.chromium.chrome.browser.settings.languages.LanguageListBaseAdapter { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.settings.privacy.ClearBrowsingDataFetcher { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.settings.privacy.ClearBrowsingDataPreferences { *; }
 -keep,allowobfuscation class org.chromium.chrome.browser.settings.privacy.ClearBrowsingDataPreferencesAdvanced { *; }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/PrerenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/PrerenderTest.java
index 3369e55..709a75e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/PrerenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/PrerenderTest.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE;
 
+import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 
@@ -66,7 +67,8 @@
     @LargeTest
     @Restriction({RESTRICTION_TYPE_NON_LOW_END_DEVICE})
     @Feature({"TabContents"})
-    @DisableIf.Build(sdk_is_greater_than = 25, message = "https://crbug.com/1014213")
+    @DisableIf.
+    Build(sdk_is_greater_than = Build.VERSION_CODES.M, message = "https://crbug.com/1014213")
     public void testNoPrerender() throws InterruptedException {
         String testUrl = mTestServer.getURL(
                 "/chrome/test/data/android/prerender/google.html");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index c2a5056..957f1f7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -66,6 +66,7 @@
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchFakeServer.MutableResolvedSearchTerm;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchInternalStateController.InternalState;
 import org.chromium.chrome.browser.contextualsearch.ResolvedSearchTerm.CardTag;
+import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler;
 import org.chromium.chrome.browser.findinpage.FindToolbar;
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
@@ -124,6 +125,11 @@
         "disable-features=" + ChromeFeatureList.CONTEXTUAL_SEARCH_ML_TAP_SUPPRESSION})
 @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
 @RetryOnFailure
+//
+// NOTE -- THIS WHOLE TEST CLASS IS TEMPORARILY DISABLED:
+//
+@DisabledTest(message = "See https://crbug.com/1058362")
+//
 public class ContextualSearchManagerTest {
     @Rule
     public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
@@ -2212,7 +2218,8 @@
     @Feature({"ContextualSearch"})
     public void testExternalNavigationWithUserGesture() {
         final ExternalNavigationHandler externalNavHandler =
-                new ExternalNavigationHandler(mActivityTestRule.getActivity().getActivityTab());
+                new ExternalNavigationHandler(new ExternalNavigationDelegateImpl(
+                        mActivityTestRule.getActivity().getActivityTab()));
         final NavigationParams navigationParams = new NavigationParams(
                 "intent://test/#Intent;scheme=test;package=com.chrome.test;end", "",
                 false /* isPost */, true /* hasUserGesture */, PageTransition.LINK,
@@ -2237,7 +2244,8 @@
     @Feature({"ContextualSearch"})
     public void testRedirectedExternalNavigationWithUserGesture() {
         final ExternalNavigationHandler externalNavHandler =
-                new ExternalNavigationHandler(mActivityTestRule.getActivity().getActivityTab());
+                new ExternalNavigationHandler(new ExternalNavigationDelegateImpl(
+                        mActivityTestRule.getActivity().getActivityTab()));
 
         final NavigationParams initialNavigationParams = new NavigationParams("http://test.com", "",
                 false /* isPost */, true /* hasUserGesture */, PageTransition.LINK,
@@ -2271,7 +2279,8 @@
     @Feature({"ContextualSearch"})
     public void testExternalNavigationWithoutUserGesture() {
         final ExternalNavigationHandler externalNavHandler =
-                new ExternalNavigationHandler(mActivityTestRule.getActivity().getActivityTab());
+                new ExternalNavigationHandler(new ExternalNavigationDelegateImpl(
+                        mActivityTestRule.getActivity().getActivityTab()));
         final NavigationParams navigationParams = new NavigationParams(
                 "intent://test/#Intent;scheme=test;package=com.chrome.test;end", "",
                 false /* isPost */, false /* hasUserGesture */, PageTransition.LINK,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
index 2224d352..f3104788 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
@@ -4,9 +4,11 @@
 
 package org.chromium.chrome.browser.externalnav;
 
+import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ResolveInfo;
+import android.net.Uri;
 import android.os.Build;
 import android.support.test.filters.SmallTest;
 
@@ -19,9 +21,11 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.instantapps.InstantAppsHandler;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.Features;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -31,8 +35,50 @@
  * Instrumentation tests for {@link ExternalNavigationHandler}.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public class ExternalNavigationDelegateImplTest {
+@CommandLineFlags
+        .Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+        @Features.DisableFeatures({ChromeFeatureList.AUTOFILL_ASSISTANT,
+                ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
+        public class ExternalNavigationDelegateImplTest {
+    private static final String AUTOFILL_ASSISTANT_INTENT_URL =
+            "intent://www.example.com#Intent;scheme=https;"
+            + "B.org.chromium.chrome.browser.autofill_assistant.ENABLED=true;"
+            + "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "="
+            + Uri.encode("https://www.example.com") + ";end";
+
+    class ExternalNavigationDelegateImplForTesting extends ExternalNavigationDelegateImpl {
+        public ExternalNavigationDelegateImplForTesting() {
+            super(mActivityTestRule.getActivity().getActivityTab());
+        }
+
+        @Override
+        public boolean isSerpReferrer() {
+            return mIsSerpReferrer;
+        }
+
+        public void setIsSerpReferrer(boolean value) {
+            mIsSerpReferrer = value;
+        }
+
+        // Convenience for testing that reduces boilerplate in constructing arguments to the
+        // production method that are common across tests.
+        public boolean handleWithAutofillAssistant(ExternalNavigationParams params) {
+            Intent intent;
+            try {
+                intent = Intent.parseUri(AUTOFILL_ASSISTANT_INTENT_URL, Intent.URI_INTENT_SCHEME);
+            } catch (Exception ex) {
+                Assert.assertTrue(false);
+                return false;
+            }
+
+            String fallbackUrl = "https://www.example.com";
+
+            return handleWithAutofillAssistant(params, intent, fallbackUrl);
+        }
+
+        private boolean mIsSerpReferrer;
+    }
+
     @Rule
     public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
             new ChromeActivityTestRule<>(ChromeActivity.class);
@@ -194,4 +240,79 @@
     public void setUp() throws InterruptedException {
         mActivityTestRule.startMainActivityOnBlankPage();
     }
+
+    @Test
+    @SmallTest
+    @Features.EnableFeatures({ChromeFeatureList.AUTOFILL_ASSISTANT,
+            ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
+    public void
+    testHandleWithAutofillAssistant_TriggersFromSearch() {
+        ExternalNavigationDelegateImplForTesting delegate =
+                new ExternalNavigationDelegateImplForTesting();
+        delegate.setIsSerpReferrer(true);
+
+        // Note: Leave the tab of |params| null to ensure that the delegate doesn't ask
+        // AutofillAssistantFacade to actually start the activity, which this test is not set up
+        // for.
+        ExternalNavigationParams params =
+                new ExternalNavigationParams
+                        .Builder(AUTOFILL_ASSISTANT_INTENT_URL, /*isIncognito=*/false)
+                        .build();
+
+        Assert.assertTrue(delegate.handleWithAutofillAssistant(params));
+    }
+
+    @Test
+    @SmallTest
+    @Features.EnableFeatures({ChromeFeatureList.AUTOFILL_ASSISTANT,
+            ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
+    public void
+    testHandleWithAutofillAssistant_DoesNotTriggerFromSearchInIncognito() {
+        ExternalNavigationDelegateImplForTesting delegate =
+                new ExternalNavigationDelegateImplForTesting();
+        delegate.setIsSerpReferrer(true);
+
+        ExternalNavigationParams params =
+                new ExternalNavigationParams
+                        .Builder(AUTOFILL_ASSISTANT_INTENT_URL, /*isIncognito=*/true)
+                        .build();
+
+        Assert.assertFalse(delegate.handleWithAutofillAssistant(params));
+    }
+
+    @Test
+    @SmallTest
+    @Features.EnableFeatures({ChromeFeatureList.AUTOFILL_ASSISTANT,
+            ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
+    public void
+    testHandleWithAutofillAssistant_DoesNotTriggerFromDifferentOrigin() {
+        ExternalNavigationDelegateImplForTesting delegate =
+                new ExternalNavigationDelegateImplForTesting();
+        delegate.setIsSerpReferrer(false);
+
+        ExternalNavigationParams params =
+                new ExternalNavigationParams
+                        .Builder(AUTOFILL_ASSISTANT_INTENT_URL, /*isIncognito=*/false)
+                        .build();
+
+        Assert.assertFalse(delegate.handleWithAutofillAssistant(params));
+    }
+
+    @Test
+    @SmallTest
+    @Features.DisableFeatures({ChromeFeatureList.AUTOFILL_ASSISTANT,
+            ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
+    public void
+    testHandleWithAutofillAssistant_DoesNotTriggerWhenFeatureDisabled() {
+        ExternalNavigationDelegateImplForTesting delegate =
+                new ExternalNavigationDelegateImplForTesting();
+        delegate.setIsSerpReferrer(true);
+
+        ExternalNavigationParams params =
+                new ExternalNavigationParams
+                        .Builder(AUTOFILL_ASSISTANT_INTENT_URL, /*isIncognito=*/false)
+                        .build();
+
+        Assert.assertFalse(delegate.handleWithAutofillAssistant(params));
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
index fae6233..08cdce73 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
@@ -62,8 +62,6 @@
         sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP)
 @Features.EnableFeatures({ChromeFeatureList.CCT_EXTERNAL_LINK_HANDLING,
         ChromeFeatureList.INTENT_BLOCK_EXTERNAL_FORM_REDIRECT_NO_GESTURE})
-@Features.DisableFeatures({ChromeFeatureList.AUTOFILL_ASSISTANT,
-        ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
 public class ExternalNavigationHandlerTest {
     // clang-format on
     @Rule
@@ -1594,11 +1592,8 @@
 
     @Test
     @SmallTest
-    @Features.EnableFeatures({ChromeFeatureList.AUTOFILL_ASSISTANT,
-            ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
-    public void
-    testAssistantAutofillIntent_catchNavigationFromGoogleSearch() {
-        mDelegate.setIsSerpReferrer(true);
+    public void testAutofillAssistantIntent_handledByDelegate() {
+        mDelegate.setHandleIntentWithAutofillAssistant(true);
 
         checkUrl(AUTOFILL_ASSISTANT_INTENT_URL)
                 .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE);
@@ -1608,11 +1603,8 @@
 
     @Test
     @SmallTest
-    @Features.EnableFeatures({ChromeFeatureList.AUTOFILL_ASSISTANT,
-            ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
-    public void
-    testAssistantAutofillIntent_doNotCatchNavigationInIncognito() {
-        mDelegate.setIsSerpReferrer(true);
+    public void testAutofillAssistantIntent_notHandledByDelegate() {
+        mDelegate.setHandleIntentWithAutofillAssistant(false);
 
         checkUrl(AUTOFILL_ASSISTANT_INTENT_URL)
                 .withIsIncognito(true)
@@ -1625,38 +1617,6 @@
 
     @Test
     @SmallTest
-    @Features.EnableFeatures({ChromeFeatureList.AUTOFILL_ASSISTANT,
-            ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
-    public void
-    testAssistantAutofillIntent_doNotCatchNavigationFromDifferentOrigin() {
-        mDelegate.setIsSerpReferrer(false);
-
-        checkUrl(AUTOFILL_ASSISTANT_INTENT_URL)
-                .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT,
-                        START_OTHER_ACTIVITY);
-
-        Assert.assertNotNull(mDelegate.startActivityIntent);
-        Assert.assertTrue(mDelegate.startActivityIntent.getScheme().startsWith("https"));
-    }
-
-    @Test
-    @SmallTest
-    @Features.DisableFeatures({ChromeFeatureList.AUTOFILL_ASSISTANT,
-            ChromeFeatureList.AUTOFILL_ASSISTANT_CHROME_ENTRY})
-    public void
-    testAssistantAutofillIntent_doNotCatchNavigationForNonEnabledFeature() {
-        mDelegate.setIsSerpReferrer(true);
-
-        checkUrl(AUTOFILL_ASSISTANT_INTENT_URL)
-                .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT,
-                        START_OTHER_ACTIVITY);
-
-        Assert.assertNotNull(mDelegate.startActivityIntent);
-        Assert.assertTrue(mDelegate.startActivityIntent.getScheme().startsWith("https"));
-    }
-
-    @Test
-    @SmallTest
     public void testIntentActionMetrics() {
         final String intentWithAction =
                 "intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;"
@@ -1950,6 +1910,12 @@
             return false;
         }
 
+        @Override
+        public boolean handleWithAutofillAssistant(
+                ExternalNavigationParams params, Intent targetIntent, String browserFallbackUrl) {
+            return mHandleWithAutofillAssistant;
+        }
+
         public void reset() {
             startActivityIntent = null;
             startIncognitoIntentCalled = false;
@@ -1993,6 +1959,10 @@
             mCanHandleWithInstantApp = value;
         }
 
+        public void setHandleIntentWithAutofillAssistant(boolean value) {
+            mHandleWithAutofillAssistant = value;
+        }
+
         public void setIsSerpReferrer(boolean value) {
             mIsSerpReferrer = value;
         }
@@ -2018,6 +1988,7 @@
         private String mNewUrlAfterClobbering;
         private String mReferrerUrlForClobbering;
         private boolean mCanHandleWithInstantApp;
+        private boolean mHandleWithAutofillAssistant;
         private boolean mIsSerpReferrer;
         private String mPreviousUrl;
         public boolean mCalledWithProxy;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
index 8b157e2..245d5fb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
@@ -194,13 +194,10 @@
         Assert.assertEquals(0, mTestObserver.updateCachedEngineCallback.getCallCount());
 
         // Accept the ToS.
-        if (freProperties.getBoolean(FirstRunActivityBase.SHOW_WELCOME_PAGE)) {
-            clickButton(mActivity, R.id.terms_accept, "Failed to accept ToS");
-            mTestObserver.jumpToPageCallback.waitForCallback(
-                    "Failed to try moving to the next screen", 0);
-            mTestObserver.acceptTermsOfServiceCallback.waitForCallback(
-                    "Failed to accept the ToS", 0);
-        }
+        clickButton(mActivity, R.id.terms_accept, "Failed to accept ToS");
+        mTestObserver.jumpToPageCallback.waitForCallback(
+                "Failed to try moving to the next screen", 0);
+        mTestObserver.acceptTermsOfServiceCallback.waitForCallback("Failed to accept the ToS", 0);
 
         // Acknowledge that Data Saver will be enabled.
         if (freProperties.getBoolean(FirstRunActivityBase.SHOW_DATA_REDUCTION_PAGE)) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareHelperTest.java
deleted file mode 100644
index c59ad01..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareHelperTest.java
+++ /dev/null
@@ -1,158 +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.
-
-package org.chromium.chrome.browser.share;
-
-import android.net.Uri;
-import android.support.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.Callback;
-import org.chromium.base.ContentUriUtils;
-import org.chromium.base.task.AsyncTask;
-import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.FileProviderHelper;
-import org.chromium.chrome.test.ChromeActivityTestRule;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.ui.base.Clipboard;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Tests of {@link ShareHelper}.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public class ShareHelperTest {
-    @Rule
-    public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
-            new ChromeActivityTestRule<>(ChromeActivity.class);
-
-    private static final long WAIT_TIMEOUT_SECONDS = 5L;
-    private static final byte[] TEST_IMAGE_DATA = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-
-    private class GenerateUriCallback extends CallbackHelper implements Callback<Uri> {
-        private Uri mImageUri;
-
-        public Uri getImageUri() {
-            return mImageUri;
-        }
-
-        @Override
-        public void onResult(Uri uri) {
-            mImageUri = uri;
-            notifyCalled();
-        }
-    }
-
-    private class AsyncTaskRunnableHelper extends CallbackHelper implements Runnable {
-        @Override
-        public void run() {
-            notifyCalled();
-        }
-    }
-
-    @Before
-    public void setUp() {
-        mActivityTestRule.startMainActivityFromLauncher();
-        ContentUriUtils.setFileProviderUtil(new FileProviderHelper());
-    }
-
-    @After
-    public void tearDown() throws TimeoutException {
-        Clipboard.getInstance().setText("");
-        clearSharedImages();
-    }
-
-    private int fileCount(File file) {
-        if (file.isFile()) {
-            return 1;
-        }
-
-        int count = 0;
-        if (file.isDirectory()) {
-            for (File f : file.listFiles()) count += fileCount(f);
-        }
-        return count;
-    }
-
-    private boolean filepathExists(File file, String filepath) {
-        if (file.isFile() && filepath.endsWith(file.getName())) {
-            return true;
-        }
-
-        if (file.isDirectory()) {
-            for (File f : file.listFiles()) {
-                if (filepathExists(f, filepath)) return true;
-            }
-        }
-        return false;
-    }
-
-    private Uri generateAnImageToClipboard() throws TimeoutException {
-        GenerateUriCallback imageCallback = new GenerateUriCallback();
-        ShareHelper.generateUriFromData(
-                mActivityTestRule.getActivity(), TEST_IMAGE_DATA, imageCallback);
-        imageCallback.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-        Clipboard.getInstance().setImageUri(imageCallback.getImageUri());
-        return imageCallback.getImageUri();
-    }
-
-    private void clearSharedImages() throws TimeoutException {
-        ShareHelper.clearSharedImages();
-
-        // ShareHelper::clearSharedImages uses AsyncTask.SERIAL_EXECUTOR to schedule a clearing the
-        // shared folder job, so schedule a new job and wait for the new job finished to make sure
-        // ShareHelper::clearSharedImages's clearing folder job finished.
-        AsyncTaskRunnableHelper runnableHelper = new AsyncTaskRunnableHelper();
-        AsyncTask.SERIAL_EXECUTOR.execute(runnableHelper);
-        runnableHelper.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-    }
-
-    private int fileCountInShareDirectory() throws IOException {
-        return fileCount(ShareHelper.getSharedFilesDirectory());
-    }
-
-    private boolean fileExistsInShareDirectory(Uri fileUri) throws IOException {
-        return filepathExists(ShareHelper.getSharedFilesDirectory(), fileUri.getPath());
-    }
-
-    @Test
-    @SmallTest
-    public void clipboardUriDoNotClearTest() throws TimeoutException, IOException {
-        generateAnImageToClipboard();
-        generateAnImageToClipboard();
-        Uri clipboardUri = generateAnImageToClipboard();
-        Assert.assertEquals(3, fileCountInShareDirectory());
-
-        clearSharedImages();
-        Assert.assertEquals(1, fileCountInShareDirectory());
-        Assert.assertTrue(fileExistsInShareDirectory(clipboardUri));
-    }
-
-    @Test
-    @SmallTest
-    public void clearEverythingIfNoClipboardImageTest() throws TimeoutException, IOException {
-        generateAnImageToClipboard();
-        generateAnImageToClipboard();
-        generateAnImageToClipboard();
-        Assert.assertEquals(3, fileCountInShareDirectory());
-
-        Clipboard.getInstance().setText("");
-        clearSharedImages();
-        Assert.assertEquals(0, fileCountInShareDirectory());
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateTest.java
index 4de76bf..c67fc38 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateTest.java
@@ -19,6 +19,7 @@
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationParams;
 import org.chromium.chrome.test.ChromeActivityTestRule;
@@ -72,7 +73,7 @@
 
     class TestExternalNavigationHandler extends ExternalNavigationHandler {
         public TestExternalNavigationHandler() {
-            super(mActivity.getActivityTab());
+            super(new ExternalNavigationDelegateImpl(mActivity.getActivityTab()));
         }
 
         @Override
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencerTest.java
index edf3ba7..9941d670 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencerTest.java
@@ -191,13 +191,12 @@
         assertFalse(mSequencer.calledSetFirstRunFlowSignInComplete);
 
         Bundle bundle = mSequencer.returnedBundle;
-        assertTrue(bundle.getBoolean(FirstRunActivityBase.SHOW_WELCOME_PAGE));
         assertTrue(bundle.getBoolean(FirstRunActivityBase.SHOW_SIGNIN_PAGE));
         assertFalse(bundle.getBoolean(FirstRunActivityBase.SHOW_DATA_REDUCTION_PAGE));
         assertFalse(bundle.getBoolean(FirstRunActivityBase.SHOW_SEARCH_ENGINE_PAGE));
         assertEquals(ChildAccountStatus.NOT_CHILD,
                 bundle.getInt(SigninFirstRunFragment.CHILD_ACCOUNT_STATUS));
-        assertEquals(5, bundle.size());
+        assertEquals(4, bundle.size());
     }
 
     @Test
@@ -219,7 +218,6 @@
         assertTrue(mSequencer.calledSetFirstRunFlowSignInComplete);
 
         Bundle bundle = mSequencer.returnedBundle;
-        assertTrue(bundle.getBoolean(FirstRunActivityBase.SHOW_WELCOME_PAGE));
         assertTrue(bundle.getBoolean(FirstRunActivityBase.SHOW_SIGNIN_PAGE));
         assertFalse(bundle.getBoolean(FirstRunActivityBase.SHOW_DATA_REDUCTION_PAGE));
         assertFalse(bundle.getBoolean(FirstRunActivityBase.SHOW_SEARCH_ENGINE_PAGE));
@@ -227,7 +225,7 @@
                 bundle.getInt(SigninFirstRunFragment.CHILD_ACCOUNT_STATUS));
         assertEquals(
                 DEFAULT_ACCOUNT, bundle.getString(SigninFirstRunFragment.FORCE_SIGNIN_ACCOUNT_TO));
-        assertEquals(6, bundle.size());
+        assertEquals(5, bundle.size());
     }
 
     @Test
@@ -249,13 +247,12 @@
         assertFalse(mSequencer.calledSetFirstRunFlowSignInComplete);
 
         Bundle bundle = mSequencer.returnedBundle;
-        assertTrue(bundle.getBoolean(FirstRunActivityBase.SHOW_WELCOME_PAGE));
         assertTrue(bundle.getBoolean(FirstRunActivityBase.SHOW_SIGNIN_PAGE));
         assertTrue(bundle.getBoolean(FirstRunActivityBase.SHOW_DATA_REDUCTION_PAGE));
         assertFalse(bundle.getBoolean(FirstRunActivityBase.SHOW_SEARCH_ENGINE_PAGE));
         assertEquals(ChildAccountStatus.NOT_CHILD,
                 bundle.getInt(SigninFirstRunFragment.CHILD_ACCOUNT_STATUS));
-        assertEquals(5, bundle.size());
+        assertEquals(4, bundle.size());
     }
 
     @Test
@@ -277,13 +274,12 @@
         assertFalse(mSequencer.calledSetFirstRunFlowSignInComplete);
 
         Bundle bundle = mSequencer.returnedBundle;
-        assertTrue(bundle.getBoolean(FirstRunActivityBase.SHOW_WELCOME_PAGE));
         assertTrue(bundle.getBoolean(FirstRunActivityBase.SHOW_SIGNIN_PAGE));
         assertTrue(bundle.getBoolean(FirstRunActivityBase.SHOW_DATA_REDUCTION_PAGE));
         assertTrue(bundle.getBoolean(FirstRunActivityBase.SHOW_SEARCH_ENGINE_PAGE));
         assertEquals(ChildAccountStatus.NOT_CHILD,
                 bundle.getInt(SigninFirstRunFragment.CHILD_ACCOUNT_STATUS));
-        assertEquals(5, bundle.size());
+        assertEquals(4, bundle.size());
     }
 
     @Test
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index e90d6f1..f1c2f828 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -1634,7 +1634,7 @@
   <message name="IDS_SETTINGS_INTERNET_TETHER_CONNECTION_NOT_NOW_BUTTON" desc="Settings > Internet > Tether Connection Dialog > Button which, when tapped, closes the dialog and does not connect the device.">
     Not now
   </message>
-  <message name="IDS_SETTINGS_INTERNET_TETHER_CONNECTION_CONNECT_BUTTON" desc="Settings > Internet > Tether Connection Dialog > Button which, when tapped, starts a connection attempt to the device.">
+ <message name="IDS_SETTINGS_INTERNET_TETHER_CONNECTION_CONNECT_BUTTON" desc="Settings > Internet > Tether Connection Dialog > Button which, when tapped, starts a connection attempt to the device.">
     Connect
   </message>
   <message name="IDS_SETTINGS_INTERNET_LOOKING_FOR_MOBILE_NETWORK" desc="Text shown when viewing the Mobile data page when there are no cellular or tether networks available.">
@@ -2298,5 +2298,88 @@
   <message name="IDS_SETTINGS_SET_DATE_TIME" desc="Label for the button that shows the dialog for setting the system date and time.">
     Set date and time
   </message>
+  
+  <!-- Device Keyboard page (OS settings) -->
+  <message name="IDS_SETTINGS_KEYBOARD_TITLE" desc="In Device Settings, the title of the keyboard settings subpage.">
+    Keyboard
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_LEFT_CTRL" desc="In Device Settings, the label and dropdown list item for the Ctrl key.">
+    Ctrl
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_LEFT_ALT" desc="In Device Settings, the label and dropdown list item for the Alt key.">
+    Alt
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_CAPS_LOCK" desc="In Device Settings, the label and dropdown list item for the Caps Lock key.">
+    Caps Lock
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_COMMAND" desc="In Device Settings, the dropdown list item for the external Command key on Apple keyboards when there's no internal keyboard on the device.">
+    Command
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_DIAMOND" desc="In Device Settings, the label for the Diamond key.">
+    Diamond
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_ESCAPE" desc="In Device Settings, the dropdown list item for the Escape key.">
+    Escape
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_BACKSPACE" desc="In Device Settings, the dropdown list item for the Backspace key.">
+    Backspace
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_ASSISTANT" desc="In Device Settings, the label and dropdown list item for the Assistant key.">
+    Assistant
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_DISABLED" desc="In Device Settings, the dropdown list item for a disabled key.">
+    Disabled
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_EXTERNAL_COMMAND" desc="In Device Settings, the dropdown list item for the external Command key on Apple keyboards.">
+    External Command
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_EXTERNAL_META" desc="In Device Settings, the dropdown list item for the external Meta key (Search key on ChromeOS keyboards, and Windows key on Windows keyboards).">
+    External Meta
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_META" desc="In Device Settings, the dropdown list item for the external Meta key (Search key on ChromeOS keyboards, and Windows key on Windows keyboards) when there's no internal keyboard on the device.">
+    Meta
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS" desc="In Device Settings, the checkbox label for interpreting the top-row keys as function keys instead.">
+    Treat top-row keys as function keys
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_ENABLE" desc="The checkbox label for enabling keyboard auto-repeat.">
+    Enable auto-repeat
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY" desc="The label for the slider for the delay before auto-repeat begins. The delay is the amount of time a key must be held down before auto-repeating begins.">
+    Delay before repeat
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY_LONG" desc="The label for the beginning of the auto-repeat delay slider, representing a long delay.">
+    Long
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY_SHORT" desc="The label for the end of the auto-repeat delay slider, representing a short delay.">
+    Short
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_RATE" desc="The label for the slider for the auto-repeat rate. The rate is how many times a key repeats per second once auto-repeating has begun.">
+    Repeat rate
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_RATE_SLOW" desc="The label for the beginning of the auto-repeat rate slider, representing a slow repeat rate.">
+    Slow
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_FAST" desc="The label for the end of the auto-repeat rate slider, representing a fast repeat rate.">
+    Fast
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_SHOW_SHORTCUT_VIEWER" desc="The link to open the keyboard shortut viewer.">
+    View keyboard shortcuts
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_SHOW_LANGUAGE_AND_INPUT" desc="The link to navigate to the language and input method settings.">
+    Change language and input settings
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_LAUNCHER" desc="In Device Settings, the label and dropdown list item for the Search key on keyboard layouts of newer Chromebooks.">
+    Launcher
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_KEY_SEARCH" desc="In Device Settings, the label and dropdown list item for the Search key.">
+    Search
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS_LAYOUT2_DESCRIPTION" desc="In Device Settings, when device uses 2017 keyboard layout, the label describing how to use the top-row keys' original actions when they are set to behave like function keys.">
+    Hold the Launcher key to switch the behavior of the top-row keys
+  </message>
+  <message name="IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS_DESCRIPTION" desc="In Device Settings, the label describing how to use the top-row keys' original actions when they are set to behave like function keys.">
+    Hold the Search key to switch the behavior of the top-row keys
+  </message>
 </if>
 </grit-part>
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index ba4710a3..9c7ea61 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2903,92 +2903,10 @@
     <message name="IDS_SETTINGS_DEVICE_TITLE" desc="Name of the settings page which displays device and peripheral settings.">
       Device
     </message>
-    <message name="IDS_SETTINGS_KEYBOARD_TITLE" desc="In Device Settings, the title of the keyboard settings subpage.">
-      Keyboard
-    </message>
     <!-- Touchpad -->
     <message name="IDS_SETTINGS_SCROLL_LABEL" desc="In Device Settings, the title above the radio buttons to choose the scrolling behavior.">
       Scrolling
     </message>
-    <!-- Keyboard -->
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_SEARCH" desc="In Device Settings, the label and dropdown list item for the Search key.">
-      Search
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_LAUNCHER" desc="In Device Settings, the label and dropdown list item for the Search key on keyboard layouts of newer Chromebooks.">
-      Launcher
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_LEFT_CTRL" desc="In Device Settings, the label and dropdown list item for the Ctrl key.">
-      Ctrl
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_LEFT_ALT" desc="In Device Settings, the label and dropdown list item for the Alt key.">
-      Alt
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_CAPS_LOCK" desc="In Device Settings, the label and dropdown list item for the Caps Lock key.">
-      Caps Lock
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_DIAMOND" desc="In Device Settings, the label for the Diamond key.">
-      Diamond
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_ESCAPE" desc="In Device Settings, the dropdown list item for the Escape key.">
-      Escape
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_BACKSPACE" desc="In Device Settings, the dropdown list item for the Backspace key.">
-      Backspace
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_ASSISTANT" desc="In Device Settings, the label and dropdown list item for the Assistant key.">
-      Assistant
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_DISABLED" desc="In Device Settings, the dropdown list item for a disabled key.">
-      Disabled
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_EXTERNAL_COMMAND" desc="In Device Settings, the dropdown list item for the external Command key on Apple keyboards.">
-      External Command
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_COMMAND" desc="In Device Settings, the dropdown list item for the external Command key on Apple keyboards when there's no internal keyboard on the device.">
-      Command
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_EXTERNAL_META" desc="In Device Settings, the dropdown list item for the external Meta key (Search key on ChromeOS keyboards, and Windows key on Windows keyboards).">
-      External Meta
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_KEY_META" desc="In Device Settings, the dropdown list item for the external Meta key (Search key on ChromeOS keyboards, and Windows key on Windows keyboards) when there's no internal keyboard on the device.">
-      Meta
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS" desc="In Device Settings, the checkbox label for interpreting the top-row keys as function keys instead.">
-      Treat top-row keys as function keys
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS_DESCRIPTION" desc="In Device Settings, the label describing how to use the top-row keys' original actions when they are set to behave like function keys.">
-      Hold the Search key to switch the behavior of the top-row keys
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS_LAYOUT2_DESCRIPTION" desc="In Device Settings, when device uses 2017 keyboard layout, the label describing how to use the top-row keys' original actions when they are set to behave like function keys.">
-      Hold the Launcher key to switch the behavior of the top-row keys
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_ENABLE" desc="The checkbox label for enabling keyboard auto-repeat.">
-      Enable auto-repeat
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY" desc="The label for the slider for the delay before auto-repeat begins. The delay is the amount of time a key must be held down before auto-repeating begins.">
-      Delay before repeat
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY_LONG" desc="The label for the beginning of the auto-repeat delay slider, representing a long delay.">
-      Long
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY_SHORT" desc="The label for the end of the auto-repeat delay slider, representing a short delay.">
-      Short
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_RATE" desc="The label for the slider for the auto-repeat rate. The rate is how many times a key repeats per second once auto-repeating has begun.">
-      Repeat rate
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_RATE_SLOW" desc="The label for the beginning of the auto-repeat rate slider, representing a slow repeat rate.">
-      Slow
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_FAST" desc="The label for the end of the auto-repeat rate slider, representing a fast repeat rate.">
-      Fast
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_SHOW_SHORTCUT_VIEWER" desc="The link to open the keyboard shortut viewer.">
-      View keyboard shortcuts
-    </message>
-    <message name="IDS_SETTINGS_KEYBOARD_SHOW_LANGUAGE_AND_INPUT" desc="The link to navigate to the language and input method settings.">
-      Change language and input settings
-    </message>
 
     <!-- Storage -->
     <message name="IDS_SETTINGS_STORAGE_TITLE" desc="In Device Settings, the title for storage management.">
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index b4748883..b66d970 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1956,6 +1956,7 @@
     "policy/status_collector/device_status_collector.h",
     "policy/status_collector/enterprise_activity_storage.cc",
     "policy/status_collector/enterprise_activity_storage.h",
+    "policy/status_collector/interval_map.h",
     "policy/status_collector/status_collector.cc",
     "policy/status_collector/status_collector.h",
     "policy/status_collector/status_collector_state.cc",
@@ -2996,6 +2997,7 @@
     "policy/secondary_google_account_signin_policy_handler_unittest.cc",
     "policy/server_backed_state_keys_broker_unittest.cc",
     "policy/single_app_install_event_log_unittest.cc",
+    "policy/status_collector/interval_map_unittest.cc",
     "policy/status_uploader_unittest.cc",
     "policy/system_log_uploader_unittest.cc",
     "policy/system_proxy_settings_policy_handler_unittest.cc",
diff --git a/chrome/browser/chromeos/policy/status_collector/interval_map.h b/chrome/browser/chromeos/policy/status_collector/interval_map.h
new file mode 100644
index 0000000..a9db01b
--- /dev/null
+++ b/chrome/browser/chromeos/policy/status_collector/interval_map.h
@@ -0,0 +1,297 @@
+// Copyright 2015 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_POLICY_STATUS_COLLECTOR_INTERVAL_MAP_H_
+#define CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_INTERVAL_MAP_H_
+
+#include <algorithm>
+#include <functional>
+#include <limits>
+#include <map>
+#include <type_traits>
+#include <utility>
+
+#include "base/logging.h"
+
+namespace policy {
+
+// An IntervalMap<KeyType, ValueType> maps every value of KeyType to
+// a ValueType, and incrementing, decrementing and setting ranges of values
+// has been optimized. The default state is to map all values in
+// KeyType to ValueType(). (Which is usually zero.)
+//
+// Set/Increment operations should generally take
+// O(log(N)) + O(M) time where N is the number of intervals in the map and
+// M is the number of modified intervals.
+//
+// Internally, IntervalMap<> uses an std::map, where the beginning of each
+// interval is stored along with the value for that interval. Adjacent intervals
+// which have the same value are automatically merged. For instance, if you did:
+//
+// IntervalMap<int, int> tmp;
+// tmp.IncrementInterval(2, 5, 2);
+// tmp.IncrementInterval(4, 6, 1);
+//
+// Then:
+//  tmp[0] = 0
+//  tmp[1] = 0
+//  tmp[2] = 2
+//  tmp[3] = 2
+//  tmp[4] = 3
+//  tmp[5] = 1
+//  tmp[6] = 0
+//
+// If you iterate over tmp, you get the following intervals:
+//  -maxint .. 2 => 0
+//  2 .. 4 => 2
+//  4 .. 5 => 3
+//  5 .. 6 => 1
+//  6 .. maxint => 0
+//
+// Internally, this would be stored in a map as:
+//    -maxint:0, 2:2, 4:3, 5:1, 6:0
+//
+// TODO(hubbe): Consider consolidating with media::Ranges.
+
+// Simple interval class.
+// Interval ends are always non-inclusive.
+// Please note that end <= begin is a valid (but empty) interval.
+template <typename T>
+struct Interval {
+ public:
+  Interval(const T& begin, const T& end) : begin(begin), end(end) {
+    static_assert(std::is_integral<T>::value, "Integral types only.");
+  }
+
+  // May return empty intervals (begin >= end).
+  Interval Intersect(const Interval& other) const {
+    return Interval(std::max(begin, other.begin), std::min(end, other.end));
+  }
+
+  bool Empty() const { return begin >= end; }
+
+  T begin;
+  T end;
+};
+
+// The IntervalMapConstIterator points to an interval in an
+// IntervalMap where all values are the same. Calling ++/--
+// goes to the next/previous interval, which is guaranteed to
+// have values different from the current interval.
+template <typename KeyType,
+          typename ValueType,
+          class Compare = std::less<KeyType>,
+          class NumericLimits = std::numeric_limits<KeyType>>
+class IntervalMapConstIterator {
+ public:
+  typedef std::map<KeyType, ValueType, Compare> MapType;
+  IntervalMapConstIterator() {
+    static_assert(std::is_integral<KeyType>::value, "Integral types only.");
+  }
+  IntervalMapConstIterator(const MapType* map,
+                           typename MapType::const_iterator iter)
+      : map_(map), iter_(iter) {
+    static_assert(std::is_integral<KeyType>::value, "Integral types only.");
+  }
+
+  bool operator==(const IntervalMapConstIterator& other) const {
+    return iter_ == other.iter_;
+  }
+
+  bool operator!=(const IntervalMapConstIterator& other) const {
+    return iter_ != other.iter_;
+  }
+
+  // Returns the beginning of the current interval.
+  KeyType interval_begin() const {
+    DCHECK(iter_ != map_->end());
+    return iter_->first;
+  }
+
+  // Returns the end of the current interval, non-inclusive.
+  KeyType interval_end() const {
+    DCHECK(iter_ != map_->end());
+    typename MapType::const_iterator next = iter_;
+    ++next;
+    if (next == map_->end()) {
+      return NumericLimits::max();
+    } else {
+      return next->first;
+    }
+  }
+
+  // Returns the current interval.
+  Interval<KeyType> interval() const {
+    return Interval<KeyType>(interval_begin(), interval_end());
+  }
+
+  // Returns the value associated with the current interval.
+  ValueType value() const {
+    DCHECK(iter_ != map_->end());
+    return iter_->second;
+  }
+
+  // Needed to make the following construct work:
+  // for (const auto& interval_value_pair : interval_map)
+  std::pair<Interval<KeyType>, ValueType> operator*() const {
+    return std::make_pair(interval(), value());
+  }
+
+  // Go to the next interval.
+  // The beginning of the next interval always matches the end of the current
+  // interval. (But should always have a different value.)
+  // Not allowed if we're already at map_->end().
+  void operator++() {
+    DCHECK(iter_ != map_->end());
+    ++iter_;
+  }
+
+  // Go to the previous interval.
+  // The end of the previous interval always matches the beginning of the
+  // current interval. (But should always have a different value.)
+  // Not allowed if we're already at map_->begin().
+  void operator--() {
+    DCHECK(iter_ != map_->begin());
+    --iter_;
+  }
+
+ private:
+  const MapType* map_;
+
+  // Pointer to the entry in the IntervalMap that specifies the
+  // beginning of the current interval.
+  typename MapType::const_iterator iter_;
+};
+
+template <typename KeyType,
+          typename ValueType,
+          class Compare = std::less<KeyType>,
+          class NumericLimits = std::numeric_limits<KeyType>>
+class IntervalMap {
+ public:
+  typedef std::map<KeyType, ValueType, Compare> MapType;
+  typedef IntervalMapConstIterator<KeyType, ValueType, Compare, NumericLimits>
+      const_iterator;
+  IntervalMap() {
+    static_assert(std::is_integral<KeyType>::value, "Integral types only.");
+    // Adding an explicit entry for the default interval is not strictly needed,
+    // but simplifies the code a lot.
+    map_[NumericLimits::min()] = ValueType();
+  }
+
+  // Returns the value at a particular point.
+  // Defaults to ValueType().
+  ValueType operator[](const KeyType& k) const {
+    typename MapType::const_iterator i = map_.upper_bound(k);
+    DCHECK(i != map_.begin());
+    --i;
+    return i->second;
+  }
+
+  // Increase [from..to) by |how_much|.
+  void IncrementInterval(KeyType from, KeyType to, ValueType how_much) {
+    if (to <= from || how_much == 0)
+      return;
+    typename MapType::iterator a = MakeEntry(from);
+    typename MapType::iterator b = MakeEntry(to);
+    for (typename MapType::iterator i = a; i != b; ++i) {
+      i->second += how_much;
+    }
+    RemoveDuplicates(a);
+    // b may be invalid
+    RemoveDuplicates(map_.lower_bound(to));
+  }
+
+  // Set [from..to) to |how_much|.
+  void SetInterval(KeyType from, KeyType to, ValueType how_much) {
+    if (to <= from)
+      return;
+    typename MapType::iterator a = MakeEntry(from);
+    typename MapType::iterator b = MakeEntry(to);
+    a->second = how_much;
+    while (true) {
+      typename MapType::iterator c = a;
+      ++c;
+      if (c == b) {
+        break;
+      } else {
+        map_.erase(c);
+      }
+    }
+    RemoveDuplicates(a);
+    // b may be invalid
+    RemoveDuplicates(map_.lower_bound(to));
+  }
+
+  // Returns an iterator to the first interval.
+  // Note, there is always at least one interval.
+  const_iterator begin() const { return const_iterator(&map(), map_.begin()); }
+
+  // Returns an end marker iterator.
+  const_iterator end() const { return const_iterator(&map(), map_.end()); }
+
+  // Returns an iterator to the interval containing |k|.
+  // Always returns a valid iterator.
+  const_iterator find(KeyType k) const {
+    typename MapType::const_iterator iter = map_.upper_bound(k);
+    DCHECK(iter != map_.begin());
+    --iter;
+    return const_iterator(&map(), iter);
+  }
+
+  bool empty() const { return map().size() == 1; }
+  void clear() {
+    map_.clear();
+    map_[NumericLimits::min()] = ValueType();
+  }
+
+ private:
+  const MapType& map() const { return map_; }
+
+  // Make an entry in map_ with the key |k| and return it's iterator.
+  // If such an entry already exists, just re-use it.
+  // If a new entry is created, it's value will be set to the same
+  // as the preceding entry, or ValueType() if no preceding entry exists.
+  // After calling this function, we'll need to call RemoveDuplicates()
+  // to clean up any duplicates that we made.
+  typename MapType::iterator MakeEntry(KeyType k) {
+    auto insert_result = map_.emplace(k, ValueType());
+    if (insert_result.second) {
+      if (insert_result.first != map_.begin()) {
+        typename MapType::iterator i = insert_result.first;
+        --i;
+        insert_result.first->second = i->second;
+      }
+    }
+    return insert_result.first;
+  }
+
+  // Remove duplicates before and after |i|.
+  void RemoveDuplicates(typename MapType::iterator i) {
+    if (i == map_.end())
+      return;
+
+    typename MapType::iterator first = i;
+    typename MapType::iterator second = i;
+    if (i != map_.begin()) {
+      --first;
+      if (first->second == second->second) {
+        map_.erase(second);
+        second = first;
+      } else {
+        first = second;
+      }
+    }
+    ++second;
+    if (second != map_.end() && first->second == second->second) {
+      map_.erase(second);
+    }
+  }
+
+  MapType map_;
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_INTERVAL_MAP_H_
diff --git a/chrome/browser/chromeos/policy/status_collector/interval_map_unittest.cc b/chrome/browser/chromeos/policy/status_collector/interval_map_unittest.cc
new file mode 100644
index 0000000..860405f
--- /dev/null
+++ b/chrome/browser/chromeos/policy/status_collector/interval_map_unittest.cc
@@ -0,0 +1,272 @@
+// Copyright 2015 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 <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/chromeos/policy/status_collector/interval_map.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Our tests only modify the interval map entries in [0..kTestSize).
+// We need this to be big enough to hit tricky corner cases, but small
+// enough that we get lots of entry duplication to clean up.
+// Also, SimpleIntervalMap uses a vector of size kTestSize to emulate
+// a intervalmap, so making this too big will the test down a lot.
+const int kTestSize = 16;
+
+class SimpleIntervalMap {
+ public:
+  SimpleIntervalMap() : data_(kTestSize) {}
+
+  void IncrementInterval(int32_t from, int32_t to, int32_t how_much) {
+    for (int32_t i = from; i < to; i++) {
+      data_[i] += how_much;
+    }
+  }
+
+  void SetInterval(int32_t from, int32_t to, int32_t how_much) {
+    for (int32_t i = from; i < to; i++) {
+      data_[i] = how_much;
+    }
+  }
+
+  int32_t operator[](int32_t index) const { return data_[index]; }
+
+ private:
+  std::vector<int32_t> data_;
+};
+
+class IntervalMapTest : public testing::Test {
+ public:
+  IntervalMapTest() = default;
+  void IncrementInterval(int32_t from, int32_t to, int32_t how_much) {
+    truth_.IncrementInterval(from, to, how_much);
+    testee_.IncrementInterval(from, to, how_much);
+    std::string message =
+        base::StringPrintf("After [%d - %d) += %d", from, to, how_much);
+    Compare(message);
+  }
+
+  void SetInterval(int32_t from, int32_t to, int32_t how_much) {
+    truth_.SetInterval(from, to, how_much);
+    testee_.SetInterval(from, to, how_much);
+    std::string message =
+        base::StringPrintf("After [%d - %d) += %d", from, to, how_much);
+    Compare(message);
+  }
+
+  // Will exercise operator[] and IntervalMap::const_iterator.
+  void Compare(const std::string& message) {
+    bool had_fail = HasFailure();
+    for (int i = 0; i < kTestSize; i++) {
+      EXPECT_EQ(truth_[i], testee_[i]) << " i = " << i << " " << message;
+    }
+    EXPECT_EQ(testee_[-1], 0) << message;
+    EXPECT_EQ(testee_[kTestSize], 0) << message;
+    int32_t prev_ = 0;
+    int32_t end_of_last_interval = 0;
+    int32_t num_intervals = 0;
+    for (const auto& r : testee_) {
+      num_intervals++;
+      EXPECT_LT(r.first.begin, r.first.end);
+      if (r.first.begin == std::numeric_limits<int32_t>::min()) {
+        EXPECT_EQ(0, r.second);
+      } else {
+        EXPECT_EQ(end_of_last_interval, r.first.begin);
+        EXPECT_GE(r.first.begin, 0) << message;
+        EXPECT_LE(r.first.begin, kTestSize) << message;
+        EXPECT_NE(r.second, prev_) << message;
+      }
+      end_of_last_interval = r.first.end;
+      prev_ = r.second;
+    }
+    EXPECT_EQ(prev_, 0) << message;
+
+    if (HasFailure() && !had_fail) {
+      for (int i = 0; i < kTestSize; i++) {
+        LOG(ERROR) << i << ": Truth =" << truth_[i]
+                   << " Testee = " << testee_[i];
+      }
+      for (const auto& r : testee_) {
+        LOG(ERROR) << "Interval:  " << r.first.begin << " - " << r.first.end
+                   << " = " << r.second;
+      }
+    }
+  }
+
+  void Clear() {
+    for (int j = 0; j < kTestSize; j++) {
+      IncrementInterval(j, j + 1, -truth_[j]);
+    }
+  }
+
+ protected:
+  SimpleIntervalMap truth_;
+  policy::IntervalMap<int32_t, int32_t> testee_;
+};
+}  // namespace
+
+TEST_F(IntervalMapTest, SimpleTest) {
+  IncrementInterval(3, 7, 4);
+  EXPECT_EQ(0, testee_[0]);
+  EXPECT_EQ(0, testee_[2]);
+  EXPECT_EQ(4, testee_[3]);
+  EXPECT_EQ(4, testee_[5]);
+  EXPECT_EQ(4, testee_[6]);
+  EXPECT_EQ(0, testee_[7]);
+  IncrementInterval(3, 7, -4);
+  EXPECT_TRUE(testee_.empty());
+}
+
+TEST_F(IntervalMapTest, SimpleIncrementTest) {
+  IncrementInterval(3, 7, 1);
+  IncrementInterval(6, 10, 2);
+  EXPECT_EQ(0, testee_[2]);
+  EXPECT_EQ(1, testee_[3]);
+  EXPECT_EQ(1, testee_[5]);
+  EXPECT_EQ(3, testee_[6]);
+  EXPECT_EQ(2, testee_[7]);
+  EXPECT_EQ(2, testee_[9]);
+  EXPECT_EQ(0, testee_[10]);
+  SetInterval(3, 12, 0);
+  EXPECT_TRUE(testee_.empty());
+}
+
+TEST_F(IntervalMapTest, IncrementJoinIntervalsTest) {
+  IncrementInterval(3, 5, 1);
+  IncrementInterval(7, 8, 1);
+  IncrementInterval(9, 11, 1);
+  IncrementInterval(5, 7, 1);
+  IncrementInterval(8, 9, 1);
+  auto i = testee_.find(5);
+  EXPECT_EQ(3, i.interval_begin());
+  EXPECT_EQ(11, i.interval_end());
+  EXPECT_EQ(1, i.value());
+}
+
+TEST_F(IntervalMapTest, SetJoinIntervalsTest) {
+  SetInterval(3, 5, 1);
+  SetInterval(7, 8, 1);
+  SetInterval(9, 11, 1);
+  SetInterval(5, 9, 1);  // overwrites one interval
+  auto i = testee_.find(5);
+  EXPECT_EQ(3, i.interval_begin());
+  EXPECT_EQ(11, i.interval_end());
+  EXPECT_EQ(1, i.value());
+}
+
+TEST_F(IntervalMapTest, FindTest) {
+  IncrementInterval(5, 6, 1);
+  IncrementInterval(1, 10, 2);
+  int32_t min_value = std::numeric_limits<int32_t>::min();
+  int32_t max_value = std::numeric_limits<int32_t>::max();
+  auto i = testee_.find(0);
+  EXPECT_EQ(min_value, i.interval_begin());
+  EXPECT_EQ(1, i.interval_end());
+  EXPECT_EQ(0, i.value());
+  i = testee_.find(4);
+  EXPECT_EQ(1, i.interval_begin());
+  EXPECT_EQ(5, i.interval_end());
+  EXPECT_EQ(2, i.value());
+  i = testee_.find(5);
+  EXPECT_EQ(5, i.interval_begin());
+  EXPECT_EQ(6, i.interval_end());
+  EXPECT_EQ(3, i.value());
+  i = testee_.find(6);
+  EXPECT_EQ(6, i.interval_begin());
+  EXPECT_EQ(10, i.interval_end());
+  EXPECT_EQ(2, i.value());
+  i = testee_.find(9);
+  EXPECT_EQ(6, i.interval_begin());
+  EXPECT_EQ(10, i.interval_end());
+  EXPECT_EQ(2, i.value());
+  i = testee_.find(10);
+  EXPECT_EQ(10, i.interval_begin());
+  EXPECT_EQ(max_value, i.interval_end());
+  EXPECT_EQ(0, i.value());
+}
+
+TEST_F(IntervalMapTest, MinMaxInt) {
+  int32_t min_value = std::numeric_limits<int32_t>::min();
+  int32_t max_value = std::numeric_limits<int32_t>::max();
+
+  // Change a single value at minint
+  testee_.IncrementInterval(min_value, min_value + 1, 7);
+  EXPECT_EQ(7, testee_[min_value]);
+  EXPECT_EQ(0, testee_[min_value + 1]);
+  auto i = testee_.find(0);
+  EXPECT_EQ(min_value + 1, i.interval_begin());
+  EXPECT_EQ(max_value, i.interval_end());
+  EXPECT_EQ(0, i.value());
+  --i;
+  EXPECT_TRUE(i == testee_.find(min_value));
+  EXPECT_EQ(min_value, i.interval_begin());
+  EXPECT_EQ(min_value + 1, i.interval_end());
+  EXPECT_EQ(7, i.value());
+  testee_.clear();
+
+  // Change a single value at maxint
+  // Note that we don't actually have a way to represent a range
+  // that includes maxint as the end of the interval is non-inclusive.
+  testee_.IncrementInterval(max_value - 1, max_value, 7);
+  EXPECT_EQ(7, testee_[max_value - 1]);
+  EXPECT_EQ(0, testee_[max_value - 2]);
+  i = testee_.find(0);
+  EXPECT_EQ(min_value, i.interval_begin());
+  EXPECT_EQ(max_value - 1, i.interval_end());
+  EXPECT_EQ(0, i.value());
+  ++i;
+  EXPECT_TRUE(i == testee_.find(max_value - 1));
+  EXPECT_EQ(max_value - 1, i.interval_begin());
+  EXPECT_EQ(max_value, i.interval_end());
+  EXPECT_EQ(7, i.value());
+
+  testee_.clear();
+
+  // Change entire range (almost)
+  testee_.IncrementInterval(min_value, max_value, 17);
+  EXPECT_EQ(17, testee_[min_value]);
+  EXPECT_EQ(17, testee_[0]);
+  EXPECT_EQ(17, testee_[max_value - 1]);
+  i = testee_.find(0);
+  EXPECT_EQ(min_value, i.interval_begin());
+  EXPECT_EQ(max_value, i.interval_end());
+  EXPECT_EQ(17, i.value());
+  EXPECT_TRUE(i == testee_.find(max_value - 1));
+  EXPECT_TRUE(i == testee_.find(min_value));
+}
+
+TEST_F(IntervalMapTest, RandomIncrementTest) {
+  for (int j = 0; j < 200; j++) {
+    Clear();
+    for (int i = 0; i < 200; i++) {
+      int32_t begin = base::RandInt(0, kTestSize - 1 - 1);
+      int32_t end = begin + 1 + base::RandInt(0, kTestSize - begin - 1 - 1);
+      IncrementInterval(begin, end, base::RandInt(0, 1) ? 1 : -1);
+      if (HasFailure()) {
+        return;
+      }
+    }
+  }
+}
+
+TEST_F(IntervalMapTest, RandomSetTest) {
+  for (int j = 0; j < 200; j++) {
+    Clear();
+    for (int i = 0; i < 200; i++) {
+      int32_t begin = base::RandInt(0, kTestSize - 1 - 1);
+      int32_t end = begin + 1 + base::RandInt(0, kTestSize - begin - 1 - 1);
+      SetInterval(begin, end, base::RandInt(0, 3));
+      if (HasFailure()) {
+        return;
+      }
+    }
+  }
+}
diff --git a/chrome/browser/download/android/BUILD.gn b/chrome/browser/download/android/BUILD.gn
index 58a25ed9..feaf309 100644
--- a/chrome/browser/download/android/BUILD.gn
+++ b/chrome/browser/download/android/BUILD.gn
@@ -8,6 +8,7 @@
   sources = [
     "java/src/org/chromium/chrome/browser/download/DirectoryOption.java",
     "java/src/org/chromium/chrome/browser/download/DownloadConstants.java",
+    "java/src/org/chromium/chrome/browser/download/DownloadDelegateImpl.java",
     "java/src/org/chromium/chrome/browser/download/DownloadDirectoryProvider.java",
     "java/src/org/chromium/chrome/browser/download/DownloadFileProvider.java",
     "java/src/org/chromium/chrome/browser/download/DownloadFilter.java",
@@ -24,6 +25,7 @@
     "//base:base_java",
     "//base:jni_java",
     "//chrome/browser/util:java",
+    "//components/download/internal/common:internal_java",
     "//components/download/public/common:public_java",
     "//components/offline_items_collection/core:core_java",
     "//content/public/android:content_java",
diff --git a/chrome/browser/download/android/DEPS b/chrome/browser/download/android/DEPS
index 077462f..73d8bc3 100644
--- a/chrome/browser/download/android/DEPS
+++ b/chrome/browser/download/android/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+media/video",
   "+content/public/android/java/src/org/chromium/content_public",
+  "+components/download/internal/common",
 ]
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadDelegateImpl.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadDelegateImpl.java
new file mode 100644
index 0000000..9dfa1597
--- /dev/null
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadDelegateImpl.java
@@ -0,0 +1,31 @@
+// 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.download;
+
+import android.net.Uri;
+
+import org.chromium.components.download.DownloadDelegate;
+
+/**
+ * Utility class that implements DownloadDelegate.
+ */
+public class DownloadDelegateImpl extends DownloadDelegate {
+    public DownloadDelegateImpl() {}
+
+    @Override
+    public String remapGenericMimeType(String mimeType, String url, String filename) {
+        return MimeUtils.remapGenericMimeType(mimeType, url, filename);
+    }
+
+    @Override
+    public Uri parseOriginalUrl(String originalUrl) {
+        return UriUtils.parseOriginalUrl(originalUrl);
+    }
+
+    @Override
+    public boolean isDownloadOnSDCard(String filePath) {
+        return DownloadDirectoryProvider.isDownloadOnSDCard(filePath);
+    }
+}
diff --git a/chrome/browser/flags/BUILD.gn b/chrome/browser/flags/BUILD.gn
index 2fbae9b..4b4ece6 100644
--- a/chrome/browser/flags/BUILD.gn
+++ b/chrome/browser/flags/BUILD.gn
@@ -9,6 +9,7 @@
     "android/java/src/org/chromium/chrome/browser/flags/BooleanCachedFieldTrialParameter.java",
     "android/java/src/org/chromium/chrome/browser/flags/CachedFieldTrialParameter.java",
     "android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java",
+    "android/java/src/org/chromium/chrome/browser/flags/IntCachedFieldTrialParameter.java",
     "android/java/src/org/chromium/chrome/browser/flags/StringCachedFieldTrialParameter.java",
   ]
   deps = [
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/IntCachedFieldTrialParameter.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/IntCachedFieldTrialParameter.java
new file mode 100644
index 0000000..6fbaff9
--- /dev/null
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/IntCachedFieldTrialParameter.java
@@ -0,0 +1,31 @@
+// 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.flags;
+
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+
+/**
+ * An int-type {@link CachedFieldTrialParameter}.
+ */
+public class IntCachedFieldTrialParameter extends CachedFieldTrialParameter {
+    private int mDefaultValue;
+
+    public IntCachedFieldTrialParameter(
+            String featureName, String variationName, int defaultValue) {
+        super(featureName, variationName, FieldTrialParameterType.INT, null);
+        mDefaultValue = defaultValue;
+    }
+
+    public int getDefaultValue() {
+        return mDefaultValue;
+    }
+
+    @Override
+    void cacheToDisk() {
+        int value = ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
+                getFeatureName(), getParameterName(), getDefaultValue());
+        SharedPreferencesManager.getInstance().writeInt(getSharedPreferenceKey(), value);
+    }
+}
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 45af542d..2e1162a 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -412,16 +412,17 @@
 #if !defined(OS_ANDROID)
   registry->RegisterBooleanPref(prefs::kShowCastIconInToolbar, false);
 #endif  // !defined(OS_ANDROID)
+  registry->RegisterTimePref(prefs::kProfileCreationTime, base::Time());
 }
 
 ProfileImpl::ProfileImpl(
     const base::FilePath& path,
     Delegate* delegate,
     CreateMode create_mode,
-    base::Time creation_time,
+    base::Time path_creation_time,
     scoped_refptr<base::SequencedTaskRunner> io_task_runner)
     : path_(path),
-      creation_time_(creation_time),
+      path_creation_time_(path_creation_time),
       io_task_runner_(std::move(io_task_runner)),
       io_data_(this),
       last_session_exit_type_(EXIT_NORMAL),
@@ -607,6 +608,15 @@
   TRACE_EVENT0("browser", "ProfileImpl::DoFinalInit")
 
   PrefService* prefs = GetPrefs();
+
+  // Do not override the existing pref in case a profile directory is copied, or
+  // if the file system does not support creation time and the property (i.e.
+  // st_ctim in posix which is actually the last status change time when the
+  // inode was last updated) use to mimic it changes because of some other
+  // modification.
+  if (!prefs->HasPrefPath(prefs::kProfileCreationTime))
+    prefs->SetTime(prefs::kProfileCreationTime, path_creation_time_);
+
   pref_change_registrar_.Init(prefs);
   pref_change_registrar_.Add(
       prefs::kSupervisedUserId,
@@ -831,7 +841,7 @@
 }
 
 base::Time ProfileImpl::GetCreationTime() const {
-  return creation_time_;
+  return prefs_->GetTime(prefs::kProfileCreationTime);
 }
 
 scoped_refptr<base::SequencedTaskRunner> ProfileImpl::GetIOTaskRunner() {
@@ -1411,7 +1421,7 @@
 #endif  // defined(OS_CHROMEOS)
 
 void ProfileImpl::SetCreationTimeForTesting(base::Time creation_time) {
-  creation_time_ = creation_time;
+  prefs_->SetTime(prefs::kProfileCreationTime, creation_time);
 }
 
 GURL ProfileImpl::GetHomePage() {
diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h
index af04facc..326e2e9 100644
--- a/chrome/browser/profiles/profile_impl.h
+++ b/chrome/browser/profiles/profile_impl.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
+#include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
@@ -181,7 +182,7 @@
   ProfileImpl(const base::FilePath& path,
               Delegate* delegate,
               CreateMode create_mode,
-              base::Time creation_time,
+              base::Time path_creation_time,
               scoped_refptr<base::SequencedTaskRunner> io_task_runner);
 
 #if defined(OS_ANDROID)
@@ -223,7 +224,7 @@
 
   base::FilePath path_;
 
-  base::Time creation_time_;
+  base::Time path_creation_time_;
 
   // Task runner used for file access in the profile path.
   scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing.js
index 1a3eecb8..cc564fb 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing.js
@@ -56,7 +56,6 @@
       // A rich text field is one where selection gets placed on a DOM
       // descendant to a root text field. This is one of:
       // - content editables (detected via richly editable state)
-      // - the node is a textarea
       //
       // The only other editables we expect are all single line (including those
       // from ARC++).
@@ -245,6 +244,14 @@
     }
     const startIndex = this.start - lineStart;
     const endIndex = this.end - lineStart;
+
+    // If the line is not the last line, and is empty, insert an explicit line
+    // break so that braille output is correctly cleared and has a position for
+    // a caret to be shown.
+    if (lineText == '' && lineIndex < this.lineBreaks_.length - 1) {
+      lineText = '\n';
+    }
+
     const spannable = new Spannable(lineText, new Output.NodeSpan(this.node_));
     ChromeVox.braille.write(
         new NavBraille({text: spannable, startIndex, endIndex}));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing_test.js
index 9cb17c4..ee42628 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing_test.js
@@ -1361,3 +1361,23 @@
         input.focus();
       });
 });
+
+TEST_F('ChromeVoxEditingTest', 'TextAreaBrailleEmptyLine', function() {
+  const mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree('<textarea></textarea>', function(root) {
+    const textarea = root.find({role: RoleType.TEXT_FIELD});
+    this.listenOnce(textarea, 'focus', function() {
+      this.listenOnce(textarea, 'valueChanged', function() {
+        mockFeedback.call(this.press(38 /* up arrow */)).expectBraille('\n');
+        mockFeedback.call(this.press(38 /* up arrow */)).expectBraille('two');
+        mockFeedback.call(this.press(38 /* up arrow */)).expectBraille('one');
+        mockFeedback.call(this.press(38 /* up arrow */)).expectBraille('\n');
+        mockFeedback.call(this.press(38 /* up arrow */))
+            .expectBraille('test mled')
+            .replay();
+      });
+    });
+    textarea.focus();
+    textarea.setValue('test\n\none\ntwo\n\nthree');
+  });
+});
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
index ebfd26b..1f96ff3 100644
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -220,10 +220,12 @@
     "a11y_page:closure_compile_module",
     "about_page:closure_compile_module",
     "appearance_page:closure_compile_module",
+    "clear_browsing_data_dialog:closure_compile_module",
     "controls:closure_compile_module",
     "downloads_page:closure_compile_module",
     "languages_page:closure_compile_module",
     "on_startup_page:closure_compile_module",
+    "people_page:closure_compile_module",
     "prefs:closure_compile_module",
     "printing_page:closure_compile_module",
     "reset_page:closure_compile_module",
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/BUILD.gn b/chrome/browser/resources/settings/clear_browsing_data_dialog/BUILD.gn
index cbe3fa9..c296d8d5 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/BUILD.gn
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/BUILD.gn
@@ -3,6 +3,9 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/polymer.gni")
+import("//ui/webui/resources/tools/js_modulizer.gni")
+import("../settings.gni")
 
 js_type_check("closure_compile") {
   deps = [
@@ -42,29 +45,33 @@
   ]
 }
 
-# TODO(crbug.com/1026426): Fix and enable.
-#js_type_check("closure_compile_module") {
-#  is_polymer3 = true
-#  deps = [
-#    ":clear_browsing_data_browser_proxy.m",
-#    ":clear_browsing_data_dialog.m",
-#    ":history_deletion_dialog.m",
-#    ":installed_app_checkbox.m",
-#  ]
-#}
+js_type_check("closure_compile_module") {
+  is_polymer3 = true
+  deps = [
+    ":clear_browsing_data_browser_proxy.m",
+    ":clear_browsing_data_dialog.m",
+    ":history_deletion_dialog.m",
+    ":installed_app_checkbox.m",
+  ]
+}
 
 js_library("clear_browsing_data_browser_proxy.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.m.js" ]
-  deps = [
-    # TODO: Fill those in.
-  ]
+  deps = [ "//ui/webui/resources/js:cr.m" ]
   extra_deps = [ ":modulize" ]
 }
 
 js_library("clear_browsing_data_dialog.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    ":clear_browsing_data_browser_proxy.m",
+    "..:route.m",
+    "..:router.m",
+    "../controls:settings_checkbox.m",
+    "../controls:settings_dropdown_menu.m",
+    "../people_page:sync_browser_proxy.m",
+    "//ui/webui/resources/js:load_time_data.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
   extra_deps = [ ":clear_browsing_data_dialog_module" ]
 }
@@ -72,7 +79,8 @@
 js_library("history_deletion_dialog.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/clear_browsing_data_dialog/history_deletion_dialog.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
   ]
   extra_deps = [ ":history_deletion_dialog_module" ]
 }
@@ -80,15 +88,14 @@
 js_library("installed_app_checkbox.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/clear_browsing_data_dialog/installed_app_checkbox.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    ":clear_browsing_data_browser_proxy.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
   extra_deps = [ ":installed_app_checkbox_module" ]
 }
 
-import("//tools/polymer/polymer.gni")
-
 group("polymer3_elements") {
-  deps = [
+  public_deps = [
     ":clear_browsing_data_dialog_module",
     ":history_deletion_dialog_module",
     ":installed_app_checkbox_module",
@@ -100,6 +107,15 @@
   js_file = "clear_browsing_data_dialog.js"
   html_file = "clear_browsing_data_dialog.html"
   html_type = "dom-module"
+  auto_imports = settings_auto_imports + [
+                   "chrome/browser/resources/settings/route.html|routes",
+                   "chrome/browser/resources/settings/router.html|Route, Router, RouteObserverBehavior",
+                   "chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.html|ClearBrowsingDataBrowserProxy, ClearBrowsingDataBrowserProxyImpl, InstalledApp",
+                   "chrome/browser/resources/settings/controls/settings_dropdown_menu.html|DropdownMenuOptionList",
+                   "chrome/browser/resources/settings/people_page/sync_browser_proxy.html|StatusAction, SyncBrowserProxy, SyncBrowserProxyImpl, SyncStatus",
+                   "ui/webui/resources/html/assert.html|assert",
+                 ]
+  namespace_rewrites = settings_namespace_rewrites
 }
 
 polymer_modulizer("history_deletion_dialog") {
@@ -112,10 +128,10 @@
   js_file = "installed_app_checkbox.js"
   html_file = "installed_app_checkbox.html"
   html_type = "dom-module"
+  auto_imports = [ "chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.html|InstalledApp" ]
 }
 
-import("//ui/webui/resources/tools/js_modulizer.gni")
-
 js_modulizer("modulize") {
   input_files = [ "clear_browsing_data_browser_proxy.js" ]
+  namespace_rewrites = settings_namespace_rewrites
 }
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.js b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.js
index 7043b50..ef587b0 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.js
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.js
@@ -7,6 +7,10 @@
  * to interact with the browser.
  */
 
+// clang-format off
+// #import {addSingletonGetter, sendWithPromise} from 'chrome://resources/js/cr.m.js';
+// clang-format on
+
 /**
  * An InstalledApp represents a domain with data that the user might want
  * to protect from being deleted.
@@ -21,11 +25,11 @@
  *   appName: string
  * }}
  */
-let InstalledApp;
+/* #export */ let InstalledApp;
 
 cr.define('settings', function() {
   /** @interface */
-  class ClearBrowsingDataBrowserProxy {
+  /* #export */ class ClearBrowsingDataBrowserProxy {
     /**
      * @param {!Array<string>} dataTypes
      * @param {number} timePeriod
@@ -55,7 +59,7 @@
   /**
    * @implements {settings.ClearBrowsingDataBrowserProxy}
    */
-  class ClearBrowsingDataBrowserProxyImpl {
+  /* #export */ class ClearBrowsingDataBrowserProxyImpl {
     /** @override */
     clearBrowsingData(dataTypes, timePeriod, installedApps) {
       return cr.sendWithPromise(
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
index 8d3a7ef..1fee615 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
@@ -4,6 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_tabs/cr_tabs.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
@@ -13,6 +14,7 @@
 <link rel="import" href="installed_app_checkbox.html">
 <link rel="import" href="../controls/settings_checkbox.html">
 <link rel="import" href="../controls/settings_dropdown_menu.html">
+<link rel="import" href="../people_page/sync_browser_proxy.html">
 <link rel="import" href="../icons.html">
 <link rel="import" href="../route.html">
 <link rel="import" href="../router.html">
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/installed_app_checkbox.html b/chrome/browser/resources/settings/clear_browsing_data_dialog/installed_app_checkbox.html
index 83e0529..7cbb798 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/installed_app_checkbox.html
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/installed_app_checkbox.html
@@ -5,6 +5,7 @@
 <link rel="import" href="../controls/settings_boolean_control_behavior.html">
 <link rel="import" href="../settings_shared_css.html">
 <link rel="import" href="../site_favicon.html">
+<link rel="import" href="./clear_browsing_data_browser_proxy.html">
 
 <dom-module id="installed-app-checkbox">
   <template>
diff --git a/chrome/browser/resources/settings/people_page/BUILD.gn b/chrome/browser/resources/settings/people_page/BUILD.gn
index 69c36a18..dcd3db1 100644
--- a/chrome/browser/resources/settings/people_page/BUILD.gn
+++ b/chrome/browser/resources/settings/people_page/BUILD.gn
@@ -3,6 +3,8 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
+import("//ui/webui/resources/tools/js_modulizer.gni")
+import("../settings.gni")
 
 js_type_check("closure_compile") {
   deps = [
@@ -159,23 +161,23 @@
 }
 
 # TODO(crbug.com/1026426): Fix and enable.
-#js_type_check("closure_compile_module") {
-#  is_polymer3 = true
-#  deps = [
-#    ":account_manager_browser_proxy.m",
-#    ":import_data_browser_proxy.m",
-#    ":import_data_dialog.m",
-#    ":manage_profile.m",
-#    ":manage_profile_browser_proxy.m",
-#    ":people_page.m",
-#    ":profile_info_browser_proxy.m",
-#    ":signout_dialog.m",
-#    ":sync_account_control.m",
-#    ":sync_browser_proxy.m",
-#    ":sync_controls.m",
-#    ":sync_page.m",
-#  ]
-#}
+js_type_check("closure_compile_module") {
+  is_polymer3 = true
+  deps = [
+    #    ":account_manager_browser_proxy.m",
+    #    ":import_data_browser_proxy.m",
+    #    ":import_data_dialog.m",
+    #    ":manage_profile.m",
+    #    ":manage_profile_browser_proxy.m",
+    #    ":people_page.m",
+    #    ":profile_info_browser_proxy.m",
+    #    ":signout_dialog.m",
+    #    ":sync_account_control.m",
+    ":sync_browser_proxy.m",
+    #    ":sync_controls.m",
+    #    ":sync_page.m",
+  ]
+}
 
 js_library("account_manager_browser_proxy.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/people_page/account_manager_browser_proxy.m.js" ]
@@ -251,9 +253,8 @@
 
 js_library("sync_browser_proxy.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/people_page/sync_browser_proxy.m.js" ]
-  deps = [
-    # TODO: Fill those in.
-  ]
+  deps = [ "//ui/webui/resources/js:cr.m" ]
+  externs_list = [ "$externs_path/metrics_private.js" ]
   extra_deps = [ ":modulize" ]
 }
 
@@ -276,7 +277,7 @@
 import("//tools/polymer/polymer.gni")
 
 group("polymer3_elements") {
-  deps = [
+  public_deps = [
     ":import_data_dialog_module",
     ":manage_profile_module",
     ":modulize",
@@ -330,8 +331,6 @@
   html_type = "dom-module"
 }
 
-import("//ui/webui/resources/tools/js_modulizer.gni")
-
 js_modulizer("modulize") {
   input_files = [
     "account_manager_browser_proxy.js",
@@ -340,4 +339,8 @@
     "profile_info_browser_proxy.js",
     "sync_browser_proxy.js",
   ]
+  namespace_rewrites = settings_namespace_rewrites + [
+                         "settings.StoredAccount|StoredAccount",
+                         "settings.SyncPrefs|SyncPrefs",
+                       ]
 }
diff --git a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
index 0e225285..336e8a2 100644
--- a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import {addSingletonGetter, sendWithPromise} from 'chrome://resources/js/cr.m.js';
+// clang-format on
+
 cr.define('settings', function() {
   /**
    * @typedef {{fullName: (string|undefined),
@@ -30,14 +34,14 @@
    *            syncSystemEnabled: (boolean|undefined)}}
    * @see chrome/browser/ui/webui/settings/people_handler.cc
    */
-  let SyncStatus;
+  /* #export */ let SyncStatus;
 
   /**
    * Must be kept in sync with the return values of getSyncErrorAction in
    * chrome/browser/ui/webui/settings/people_handler.cc
    * @enum {string}
    */
-  const StatusAction = {
+  /* #export */ const StatusAction = {
     NO_ACTION: 'noAction',             // No action to take.
     REAUTHENTICATE: 'reauthenticate',  // User needs to reauthenticate.
     SIGNOUT_AND_SIGNIN:
@@ -88,7 +92,7 @@
   let SyncPrefs;
 
   /** @enum {string} */
-  const PageStatus = {
+  /* #export */ const PageStatus = {
     SPINNER: 'spinner',      // Before the page has loaded.
     CONFIGURE: 'configure',  // Preferences ready to be configured.
     DONE: 'done',            // Sync subpage can be closed now.
@@ -107,7 +111,7 @@
   const PROMO_IMPRESSION_COUNT_KEY = 'signin-promo-count';
 
   /** @interface */
-  class SyncBrowserProxy {
+  /* #export */ class SyncBrowserProxy {
     // <if expr="not chromeos">
     /**
      * Starts the signin process for the user. Does nothing if the user is
@@ -232,7 +236,7 @@
   /**
    * @implements {settings.SyncBrowserProxy}
    */
-  class SyncBrowserProxyImpl {
+  /* #export */ class SyncBrowserProxyImpl {
     // <if expr="not chromeos">
     /** @override */
     startSignIn() {
diff --git a/chrome/browser/resources/settings/settings.gni b/chrome/browser/resources/settings/settings.gni
index fda1b4b3..86279ac 100644
--- a/chrome/browser/resources/settings/settings.gni
+++ b/chrome/browser/resources/settings/settings.gni
@@ -11,6 +11,7 @@
   "settings.CaptionsBrowserProxy|CaptionsBrowserProxy",
   "settings.ChromeCleanupProxy|ChromeCleanupProxy",
   "settings.ChromeCleanupRemovalListItem|ChromeCleanupRemovalListItem",
+  "settings.ClearBrowsingDataBrowserProxy|ClearBrowsingDataBrowserProxy",
   "settings.DefaultBrowserBrowserProxy|DefaultBrowserBrowserProxy",
   "settings.DownloadsBrowserProxy|DownloadsBrowserProxy",
   "settings.EDIT_STARTUP_URL_EVENT|EDIT_STARTUP_URL_EVENT",
@@ -24,6 +25,7 @@
   "settings.LifetimeBrowserProxy|LifetimeBrowserProxy",
   "settings.MinimumRoutes|MinimumRoutes",
   "settings.OnStartupBrowserProxy|OnStartupBrowserProxy",
+  "settings.PageStatus|PageStatus",
   "settings.pageVisibility|pageVisibility",
   "Settings.PrefUtil.prefToString|prefToString",
   "Settings.PrefUtil.stringToPrefValue|stringToPrefValue",
@@ -34,6 +36,9 @@
   "settings.SearchEnginesBrowserProxy|SearchEnginesBrowserProxy",
   "settings.SearchRequest|SearchRequest",
   "settings.StartupUrlsPageBrowserProxy|StartupUrlsPageBrowserProxy",
+  "settings.StatusAction|StatusAction",
+  "settings.SyncBrowserProxy|SyncBrowserProxy",
+  "settings.SyncStatus|SyncStatus",
   "settings.SystemPageBrowserProxy|SystemPageBrowserProxy",
   "settings.RouteObserverBehavior|RouteObserverBehavior",
 
diff --git a/chrome/browser/resources/settings/settings.js b/chrome/browser/resources/settings/settings.js
index 3331aad..f5b89157 100644
--- a/chrome/browser/resources/settings/settings.js
+++ b/chrome/browser/resources/settings/settings.js
@@ -6,6 +6,7 @@
 import './about_page/about_page.m.js';
 import './appearance_page/appearance_page.m.js';
 import './appearance_page/appearance_fonts_page.m.js';
+import './clear_browsing_data_dialog/clear_browsing_data_dialog.m.js';
 import './controls/controlled_button.m.js';
 import './controls/controlled_radio_button.m.js';
 import './controls/extension_controlled_indicator.m.js';
@@ -57,6 +58,7 @@
 
 export {AboutPageBrowserProxy, AboutPageBrowserProxyImpl, UpdateStatus} from './about_page/about_page_browser_proxy.m.js';
 export {AppearanceBrowserProxy, AppearanceBrowserProxyImpl} from './appearance_page/appearance_browser_proxy.m.js';
+export {ClearBrowsingDataBrowserProxyImpl} from './clear_browsing_data_dialog/clear_browsing_data_browser_proxy.m.js';
 export {CrSettingsPrefs} from './prefs/prefs_types.m.js';
 export {DownloadsBrowserProxyImpl} from './downloads_page/downloads_browser_proxy.m.js';
 export {ExtensionControlBrowserProxyImpl} from './extension_control_browser_proxy.m.js';
@@ -68,6 +70,7 @@
 export {OnStartupBrowserProxy, OnStartupBrowserProxyImpl} from './on_startup_page/on_startup_browser_proxy.m.js';
 export {EDIT_STARTUP_URL_EVENT} from './on_startup_page/startup_url_entry.m.js';
 export {StartupUrlsPageBrowserProxy, StartupUrlsPageBrowserProxyImpl} from './on_startup_page/startup_urls_page_browser_proxy.m.js';
+export {PageStatus, StatusAction, SyncBrowserProxyImpl} from './people_page/sync_browser_proxy.m.js';
 export {pageVisibility} from './page_visibility.m.js';
 export {prefToString, stringToPrefValue} from './prefs/pref_util.m.js';
 export {routes} from './route.m.js';
diff --git a/chrome/browser/resources/settings/settings_resources_v3.grdp b/chrome/browser/resources/settings/settings_resources_v3.grdp
index e9da18ff..5217aa16 100644
--- a/chrome/browser/resources/settings/settings_resources_v3.grdp
+++ b/chrome/browser/resources/settings/settings_resources_v3.grdp
@@ -65,6 +65,23 @@
              use_base_dir="false"
              type="BINDATA" />
   </if>
+  <include name="IDR_SETTINGS_CLEAR_BROWSING_DATA_DIALOG_CLEAR_BROWSING_DATA_BROWSER_PROXY_M_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_browser_proxy.m.js"
+           use_base_dir="false"
+           type="BINDATA" />
+  <include name="IDR_SETTINGS_CLEAR_BROWSING_DATA_DIALOG_CLEAR_BROWSING_DATA_DIALOG_M_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.m.js"
+           use_base_dir="false"
+           type="BINDATA"
+           preprocess="true" />
+  <include name="IDR_SETTINGS_CLEAR_BROWSING_DATA_DIALOG_HISTORY_DELETION_DIALOG_M_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/clear_browsing_data_dialog/history_deletion_dialog.m.js"
+           use_base_dir="false"
+           type="BINDATA" />
+  <include name="IDR_SETTINGS_CLEAR_BROWSING_DATA_DIALOG_INSTALLED_APP_CHECKBOX_M_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/clear_browsing_data_dialog/installed_app_checkbox.m.js"
+           use_base_dir="false"
+           type="BINDATA" />
   <include name="IDR_SETTINGS_CONTROLS_CONTROLLED_BUTTON_M_JS"
            file="${root_gen_dir}/chrome/browser/resources/settings/controls/controlled_button.m.js"
            use_base_dir="false"
@@ -361,6 +378,11 @@
            file="${root_gen_dir}/chrome/browser/resources/settings/site_favicon.m.js"
            use_base_dir="false"
            type="BINDATA" />
+  <include name="IDR_SETTINGS_PEOPLE_PAGE_SYNC_BROWSER_PROXY_M_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/people_page/sync_browser_proxy.m.js"
+           use_base_dir="false"
+           type="BINDATA"
+           preprocess="true" />
   <if expr="not chromeos">
     <include name="IDR_SETTINGS_PRINTING_PAGE_PRINTING_BROWSER_PROXY_M_JS"
              file="${root_gen_dir}/chrome/browser/resources/settings/printing_page/printing_browser_proxy.m.js"
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index 5637a76..3324ad38 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -99,6 +99,7 @@
     ]
     deps += [
       ":url_lookup_service_factory",
+      ":verdict_cache_manager_factory",
       "//chrome/browser/engagement:mojo_bindings",
       "//chrome/common/safe_browsing:proto",
       "//components/safe_browsing/content",
@@ -279,6 +280,7 @@
   ]
 
   deps = [
+    ":verdict_cache_manager_factory",
     "//components/keyed_service/content",
     "//components/safe_browsing/core/realtime:url_lookup_service",
     "//components/signin/public/identity_manager",
@@ -286,6 +288,22 @@
   ]
 }
 
+source_set("verdict_cache_manager_factory") {
+  sources = [
+    "verdict_cache_manager_factory.cc",
+    "verdict_cache_manager_factory.h",
+  ]
+
+  deps = [
+    "//components/content_settings/core/browser",
+    "//components/history/core/browser",
+    "//components/keyed_service/content",
+    "//components/prefs",
+    "//components/safe_browsing/core:verdict_cache_manager",
+    "//content/public/browser",
+  ]
+}
+
 static_library("advanced_protection") {
   sources = [
     "advanced_protection_status_manager.cc",
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index 92a3cd2..c1a8c1b 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
+#include "chrome/browser/safe_browsing/verdict_cache_manager_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/user_event_service_factory.h"
@@ -234,7 +235,7 @@
       profile_(profile),
       navigation_observer_manager_(sb_service->navigation_observer_manager()),
       pref_change_registrar_(new PrefChangeRegistrar),
-      cache_manager_(sb_service->GetVerdictCacheManager(profile)) {
+      cache_manager_(VerdictCacheManagerFactory::GetForProfile(profile)) {
   pref_change_registrar_->Init(profile_->GetPrefs());
 
 #if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_browsertest_base.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_browsertest_base.cc
index eb79e37d..74bcbc4 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_browsertest_base.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_browsertest_base.cc
@@ -29,9 +29,7 @@
 DeepScanningBrowserTestBase::DeepScanningBrowserTestBase() {
   // Enable every deep scanning features.
   scoped_feature_list_.InitWithFeatures(
-      {kContentComplianceEnabled, kMalwareScanEnabled,
-       kDeepScanningOfUploadsUI},
-      {});
+      {kContentComplianceEnabled, kMalwareScanEnabled}, {});
 
   // Change the time values of the upload UI to smaller ones to make tests
   // showing it run faster.
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
index 8be3dbf..97db02c 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
@@ -50,10 +50,6 @@
 
 namespace safe_browsing {
 
-// TODO(rogerta): keeping this disabled by default until UX is finalized.
-const base::Feature kDeepScanningOfUploadsUI{
-    "SafeBrowsingDeepScanningOfUploadsUI", base::FEATURE_DISABLED_BY_DEFAULT};
-
 namespace {
 
 // Global pointer of factory function (RepeatingCallback) used to create
@@ -205,6 +201,11 @@
          state != BLOCK_UNSUPPORTED_FILETYPES_UPLOADS_AND_DOWNLOADS;
 }
 
+bool* UIEnabledStorage() {
+  static bool enabled = true;
+  return &enabled;
+}
+
 }  // namespace
 
 // A BinaryUploadService::Request implementation that gets the data to scan
@@ -416,9 +417,8 @@
   bool work_being_done = delegate->UploadData();
 
   // Only show UI if work is being done in the background, the user must
-  // wait for a verdict, and the UI feature is enabled.
-  bool show_ui = work_being_done && wait_for_verdict &&
-                 base::FeatureList::IsEnabled(kDeepScanningOfUploadsUI);
+  // wait for a verdict.
+  bool show_ui = work_being_done && wait_for_verdict && (*UIEnabledStorage());
 
   // If the UI is enabled, create the modal dialog.
   if (show_ui) {
@@ -457,6 +457,11 @@
     GetFactoryStorage()->Reset();
 }
 
+// static
+void DeepScanningDialogDelegate::DisableUIForTesting() {
+  *UIEnabledStorage() = false;
+}
+
 DeepScanningDialogDelegate::DeepScanningDialogDelegate(
     content::WebContents* web_contents,
     Data data,
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
index 509d38f4..19e252c1 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
@@ -31,8 +31,6 @@
 
 class DeepScanningDialogViews;
 
-extern const base::Feature kDeepScanningOfUploadsUI;
-
 // A tab modal dialog delegate that informs the user of a background deep
 // scan happening in the given tab with an option to cancel the operation.
 //
@@ -202,6 +200,9 @@
   static void SetFactoryForTesting(Factory factory);
   static void ResetFactoryForTesting();
 
+  // Showing the UI is not possible in unit tests, call this to disable it.
+  static void DisableUIForTesting();
+
   // Determines if a request result should be used to allow a data use or to
   // block it.
   static bool ResultShouldAllowDataUse(BinaryUploadService::Result result);
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
index 576b03b8..d77ceae 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
@@ -59,6 +59,7 @@
   BaseTest() : profile_manager_(TestingBrowserProcess::GetGlobal()) {
     EXPECT_TRUE(profile_manager_.SetUp());
     profile_ = profile_manager_.CreateTestingProfile("test-user");
+    DeepScanningDialogDelegate::DisableUIForTesting();
   }
 
   void EnableFeatures(const std::vector<base::Feature>& features) {
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index c8585d5..7e2c981 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -45,7 +45,6 @@
 #include "components/safe_browsing/core/ping_manager.h"
 #include "components/safe_browsing/core/realtime/policy_engine.h"
 #include "components/safe_browsing/core/triggers/trigger_manager.h"
-#include "components/safe_browsing/core/verdict_cache_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/cross_thread_pending_shared_url_loader_factory.h"
@@ -241,13 +240,6 @@
       cmdline->HasSwitch(::switches::kDisableBackgroundNetworking));
 }
 
-VerdictCacheManager* SafeBrowsingService::GetVerdictCacheManager(
-    Profile* profile) const {
-  if (profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled))
-    return services_delegate_->GetVerdictCacheManager(profile);
-  return nullptr;
-}
-
 BinaryUploadService* SafeBrowsingService::GetBinaryUploadService(
     Profile* profile) const {
   return services_delegate_->GetBinaryUploadService(profile);
@@ -367,14 +359,12 @@
 
 void SafeBrowsingService::OnProfileWillBeDestroyed(Profile* profile) {
   observed_profiles_.Remove(profile);
-  services_delegate_->RemoveVerdictCacheManager(profile);
   services_delegate_->RemovePasswordProtectionService(profile);
   services_delegate_->RemoveTelemetryService(profile);
   services_delegate_->RemoveBinaryUploadService(profile);
 }
 
 void SafeBrowsingService::CreateServicesForProfile(Profile* profile) {
-  services_delegate_->CreateVerdictCacheManager(profile);
   services_delegate_->CreatePasswordProtectionService(profile);
   services_delegate_->CreateTelemetryService(profile);
   services_delegate_->CreateBinaryUploadService(profile);
diff --git a/chrome/browser/safe_browsing/services_delegate.cc b/chrome/browser/safe_browsing/services_delegate.cc
index 5dfc28f..1c5b8a3a 100644
--- a/chrome/browser/safe_browsing/services_delegate.cc
+++ b/chrome/browser/safe_browsing/services_delegate.cc
@@ -18,7 +18,6 @@
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/safe_browsing/buildflags.h"
 #include "components/safe_browsing/core/db/v4_local_database_manager.h"
-#include "components/safe_browsing/core/verdict_cache_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/preferences/public/mojom/tracked_preference_validation_delegate.mojom.h"
@@ -62,37 +61,7 @@
                                                       : nullptr;
 }
 
-void ServicesDelegate::CreateVerdictCacheManager(Profile* profile) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(profile);
-  auto it = cache_manager_map_.find(profile);
-  DCHECK(it == cache_manager_map_.end());
-  auto cache_manager = std::make_unique<VerdictCacheManager>(
-      HistoryServiceFactory::GetForProfile(profile,
-                                           ServiceAccessType::EXPLICIT_ACCESS),
-      HostContentSettingsMapFactory::GetForProfile(profile));
-  cache_manager_map_[profile] = std::move(cache_manager);
-}
-
-void ServicesDelegate::RemoveVerdictCacheManager(Profile* profile) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(profile);
-  auto it = cache_manager_map_.find(profile);
-  if (it != cache_manager_map_.end())
-    cache_manager_map_.erase(it);
-}
-
-VerdictCacheManager* ServicesDelegate::GetVerdictCacheManager(
-    Profile* profile) const {
-  DCHECK(profile);
-  auto it = cache_manager_map_.find(profile);
-  return it != cache_manager_map_.end() ? it->second.get() : nullptr;
-}
-
 void ServicesDelegate::ShutdownServices() {
-  // Delete the VerdictCacheManager instances
-  cache_manager_map_.clear();
-
   // Delete the ChromePasswordProtectionService instances.
   password_protection_service_map_.clear();
 }
diff --git a/chrome/browser/safe_browsing/services_delegate.h b/chrome/browser/safe_browsing/services_delegate.h
index 7a4ab3e1..f42db2e 100644
--- a/chrome/browser/safe_browsing/services_delegate.h
+++ b/chrome/browser/safe_browsing/services_delegate.h
@@ -41,7 +41,6 @@
 class SafeBrowsingService;
 class SafeBrowsingDatabaseManager;
 struct V4ProtocolConfig;
-class VerdictCacheManager;
 
 // Abstraction to help organize code for mobile vs full safe browsing modes.
 // This helper class should be owned by a SafeBrowsingService, and it handles
@@ -130,10 +129,6 @@
   virtual void CreateTelemetryService(Profile* profile) {}
   virtual void RemoveTelemetryService(Profile* profile) {}
 
-  virtual void CreateVerdictCacheManager(Profile* profile);
-  virtual void RemoveVerdictCacheManager(Profile* profile);
-  virtual VerdictCacheManager* GetVerdictCacheManager(Profile* profile) const;
-
   virtual void CreateBinaryUploadService(Profile* profile) = 0;
   virtual void RemoveBinaryUploadService(Profile* profile) = 0;
   virtual BinaryUploadService* GetBinaryUploadService(
@@ -154,10 +149,6 @@
   base::flat_map<Profile*, std::unique_ptr<ChromePasswordProtectionService>>
       password_protection_service_map_;
 
-  // Tracks existing Profiles, and their corresponding VerdictCacheManager
-  // instances. Accessed on UI thread.
-  base::flat_map<Profile*, std::unique_ptr<VerdictCacheManager>>
-      cache_manager_map_;
 };
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/url_lookup_service_factory.cc b/chrome/browser/safe_browsing/url_lookup_service_factory.cc
index 8077b5a..5336c943 100644
--- a/chrome/browser/safe_browsing/url_lookup_service_factory.cc
+++ b/chrome/browser/safe_browsing/url_lookup_service_factory.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/safe_browsing/verdict_cache_manager_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -36,6 +37,7 @@
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(IdentityManagerFactory::GetInstance());
   DependsOn(ProfileSyncServiceFactory::GetInstance());
+  DependsOn(VerdictCacheManagerFactory::GetInstance());
 }
 
 KeyedService* RealTimeUrlLookupServiceFactory::BuildServiceInstanceFor(
@@ -47,17 +49,10 @@
   auto url_loader_factory =
       std::make_unique<network::CrossThreadPendingSharedURLLoaderFactory>(
           g_browser_process->safe_browsing_service()->GetURLLoaderFactory());
-  // When |url_lookup_service| constructs, |cache_manager| is already
-  // constructed. Because |cache_manager| is constructed by |services_delegate|
-  // when the profile is created and |url_lookup_service| is constructed
-  // when the navigation starts.
-  VerdictCacheManager* cache_manager =
-      g_browser_process->safe_browsing_service()->GetVerdictCacheManager(
-          profile);
-  DCHECK(cache_manager);
   return new RealTimeUrlLookupService(
       network::SharedURLLoaderFactory::Create(std::move(url_loader_factory)),
-      cache_manager, IdentityManagerFactory::GetForProfile(profile),
+      VerdictCacheManagerFactory::GetForProfile(profile),
+      IdentityManagerFactory::GetForProfile(profile),
       ProfileSyncServiceFactory::GetForProfile(profile), profile->GetPrefs(),
       profile->IsOffTheRecord());
 }
diff --git a/chrome/browser/safe_browsing/verdict_cache_manager_factory.cc b/chrome/browser/safe_browsing/verdict_cache_manager_factory.cc
new file mode 100644
index 0000000..a6d6d2c
--- /dev/null
+++ b/chrome/browser/safe_browsing/verdict_cache_manager_factory.cc
@@ -0,0 +1,52 @@
+// 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/safe_browsing/verdict_cache_manager_factory.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
+#include "content/public/browser/browser_context.h"
+
+namespace safe_browsing {
+
+// static
+VerdictCacheManager* VerdictCacheManagerFactory::GetForProfile(
+    Profile* profile) {
+  return static_cast<VerdictCacheManager*>(
+      GetInstance()->GetServiceForBrowserContext(profile, /* create= */ true));
+}
+
+// static
+VerdictCacheManagerFactory* VerdictCacheManagerFactory::GetInstance() {
+  return base::Singleton<VerdictCacheManagerFactory>::get();
+}
+
+VerdictCacheManagerFactory::VerdictCacheManagerFactory()
+    : BrowserContextKeyedServiceFactory(
+          "VerdictCacheManager",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(HistoryServiceFactory::GetInstance());
+  DependsOn(HostContentSettingsMapFactory::GetInstance());
+}
+
+KeyedService* VerdictCacheManagerFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  Profile* profile = Profile::FromBrowserContext(context);
+  return new VerdictCacheManager(
+      HistoryServiceFactory::GetForProfile(profile,
+                                           ServiceAccessType::EXPLICIT_ACCESS),
+      HostContentSettingsMapFactory::GetForProfile(profile));
+}
+
+content::BrowserContext* VerdictCacheManagerFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextOwnInstanceInIncognito(context);
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/verdict_cache_manager_factory.h b/chrome/browser/safe_browsing/verdict_cache_manager_factory.h
new file mode 100644
index 0000000..f622e33
--- /dev/null
+++ b/chrome/browser/safe_browsing/verdict_cache_manager_factory.h
@@ -0,0 +1,51 @@
+// 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_SAFE_BROWSING_VERDICT_CACHE_MANAGER_FACTORY_H_
+#define CHROME_BROWSER_SAFE_BROWSING_VERDICT_CACHE_MANAGER_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class KeyedService;
+class Profile;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace safe_browsing {
+
+class VerdictCacheManager;
+
+// Singleton that owns VerdictCacheManager objects, one for each active
+// Profile. It listens to profile destroy events and destroy its associated
+// service. It returns a separate instance if the profile is in the Incognito
+// mode.
+class VerdictCacheManagerFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  // Creates the service if it doesn't exist already for the given |profile|.
+  // If the service already exists, return its pointer.
+  static VerdictCacheManager* GetForProfile(Profile* profile);
+
+  // Get the singleton instance.
+  static VerdictCacheManagerFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<VerdictCacheManagerFactory>;
+
+  VerdictCacheManagerFactory();
+  ~VerdictCacheManagerFactory() override = default;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(VerdictCacheManagerFactory);
+};
+
+}  // namespace safe_browsing
+#endif  // CHROME_BROWSER_SAFE_BROWSING_VERDICT_CACHE_MANAGER_FACTORY_H_
diff --git a/chrome/browser/safe_browsing/verdict_cache_manager_factory_unittest.cc b/chrome/browser/safe_browsing/verdict_cache_manager_factory_unittest.cc
new file mode 100644
index 0000000..6b821a1f
--- /dev/null
+++ b/chrome/browser/safe_browsing/verdict_cache_manager_factory_unittest.cc
@@ -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.
+
+#include "chrome/browser/safe_browsing/verdict_cache_manager_factory.h"
+
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+
+// Check that VerdictCacheManagerFactory returns different object
+// for off-the-record profile and regular profile.
+TEST(VerdictCacheManagerFactoryTest, OffTheRecordUseDifferentService) {
+  content::BrowserTaskEnvironment task_environment;
+
+  TestingProfile::Builder builder;
+  std::unique_ptr<TestingProfile> testing_profile = builder.Build();
+
+  // There should be a not null object for off-the-record profile.
+  EXPECT_NE(nullptr, VerdictCacheManagerFactory::GetForProfile(
+                         testing_profile->GetOffTheRecordProfile()));
+
+  EXPECT_NE(VerdictCacheManagerFactory::GetForProfile(testing_profile.get()),
+            VerdictCacheManagerFactory::GetForProfile(
+                testing_profile->GetOffTheRecordProfile()));
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/ui/ash/back_gesture_contextual_nudge_delegate.cc b/chrome/browser/ui/ash/back_gesture_contextual_nudge_delegate.cc
index d7515f7e..0695ffc 100644
--- a/chrome/browser/ui/ash/back_gesture_contextual_nudge_delegate.cc
+++ b/chrome/browser/ui/ash/back_gesture_contextual_nudge_delegate.cc
@@ -46,8 +46,14 @@
 void BackGestureContextualNudgeDelegate::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   DCHECK(window_);
-  if (navigation_handle->HasCommitted())
+  // Make sure for one valid navigation, we only fire one status change
+  // notification.
+  if (navigation_handle->HasCommitted() &&
+      (navigation_handle->IsInMainFrame() ||
+       navigation_handle->HasSubframeNavigationEntryCommitted()) &&
+      (navigation_handle->GetURL() != navigation_handle->GetPreviousURL())) {
     controller_->NavigationEntryChanged(window_);
+  }
 }
 
 void BackGestureContextualNudgeDelegate::OnTabStripModelChanged(
diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc
index 7803de6..c8522bc 100644
--- a/chrome/browser/ui/startup/bad_flags_prompt.cc
+++ b/chrome/browser/ui/startup/bad_flags_prompt.cc
@@ -38,6 +38,7 @@
 #include "media/media_buildflags.h"
 #include "services/network/public/cpp/network_switches.h"
 #include "services/service_manager/sandbox/switches.h"
+#include "third_party/blink/public/common/features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 
@@ -141,6 +142,7 @@
 // Dangerous feature flags in about:flags for which to display a warning that
 // "stability and security will suffer".
 static const base::Feature* kBadFeatureFlagsInAboutFlags[] = {
+    &blink::features::kRawClipboard,
     &features::kAllowSignedHTTPExchangeCertsWithoutExtension,
     &features::kWebBundlesFromNetwork,
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc b/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
index 03add719..79a423c3 100644
--- a/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
+++ b/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
@@ -80,6 +80,7 @@
             &safe_browsing::FakeDeepScanningDialogDelegate::Create,
             run_loop_->QuitClosure(), callback, is_encrypted_callback,
             "dm_token"));
+    safe_browsing::DeepScanningDialogDelegate::DisableUIForTesting();
   }
 
   // Common code for running the test cases.
diff --git a/chrome/browser/ui/views/external_protocol_dialog_browsertest.cc b/chrome/browser/ui/views/external_protocol_dialog_browsertest.cc
index 6fe13798..6bcc57d 100644
--- a/chrome/browser/ui/views/external_protocol_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/external_protocol_dialog_browsertest.cc
@@ -110,7 +110,7 @@
 
 IN_PROC_BROWSER_TEST_F(ExternalProtocolDialogBrowserTest, TestAccept) {
   ShowUi(std::string());
-  EXPECT_TRUE(dialog_->Accept());
+  dialog_->AcceptDialog();
   EXPECT_EQ(blocked_state_, BlockState::UNKNOWN);
   EXPECT_TRUE(url_did_launch_);
   histogram_tester_.ExpectBucketCount(
@@ -122,7 +122,7 @@
                        TestAcceptWithChecked) {
   ShowUi(std::string());
   SetChecked(true);
-  EXPECT_TRUE(dialog_->Accept());
+  dialog_->AcceptDialog();
   EXPECT_EQ(blocked_scheme_, "telnet");
   EXPECT_EQ(blocked_state_, BlockState::DONT_BLOCK);
   EXPECT_TRUE(url_did_launch_);
@@ -138,7 +138,7 @@
   ShowUi(std::string());
   SetChecked(true);  // |remember_| must be true for the segfault to occur.
   browser()->tab_strip_model()->CloseAllTabs();
-  EXPECT_TRUE(dialog_->Accept());
+  dialog_->AcceptDialog();
   EXPECT_FALSE(url_did_launch_);
   histogram_tester_.ExpectBucketCount(
       ExternalProtocolHandler::kHandleStateMetric,
@@ -147,7 +147,7 @@
 
 IN_PROC_BROWSER_TEST_F(ExternalProtocolDialogBrowserTest, TestCancel) {
   ShowUi(std::string());
-  EXPECT_TRUE(dialog_->Cancel());
+  dialog_->CancelDialog();
   EXPECT_EQ(blocked_state_, BlockState::UNKNOWN);
   EXPECT_FALSE(url_did_launch_);
   histogram_tester_.ExpectBucketCount(
@@ -159,7 +159,7 @@
                        TestCancelWithChecked) {
   ShowUi(std::string());
   SetChecked(true);
-  EXPECT_TRUE(dialog_->Cancel());
+  dialog_->CancelDialog();
   // Cancel() should not enforce the remember checkbox.
   EXPECT_EQ(blocked_state_, BlockState::UNKNOWN);
   EXPECT_FALSE(url_did_launch_);
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index 5e2830d..a4e7cf8 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -779,23 +779,21 @@
 
 void ProfileMenuViewBase::UpdateSyncInfoContainerBackground() {
   ui::NativeTheme::ColorId bg_color;
-  SkAlpha alpha = 16;
   switch (sync_background_state_) {
     case SyncInfoContainerBackgroundState::kNoError:
       sync_info_container_->SetBackground(nullptr);
       return;
     case SyncInfoContainerBackgroundState::kPaused:
-      bg_color = ui::NativeTheme::kColorId_ProminentButtonColor;
+      bg_color = ui::NativeTheme::kColorId_SyncInfoContainerPaused;
       break;
     case SyncInfoContainerBackgroundState::kError:
-      bg_color = ui::NativeTheme::kColorId_AlertSeverityHigh;
+      bg_color = ui::NativeTheme::kColorId_SyncInfoContainerError;
       break;
     case SyncInfoContainerBackgroundState::kNoPrimaryAccount:
-      bg_color = ui::NativeTheme::kColorId_HighlightedMenuItemBackgroundColor;
-      alpha = SK_AlphaOPAQUE;
+      bg_color = ui::NativeTheme::kColorId_SyncInfoContainerNoPrimaryAccount;
   }
   sync_info_container_->SetBackground(views::CreateRoundedRectBackground(
-      SkColorSetA(GetNativeTheme()->GetSystemColor(bg_color), alpha),
+      GetNativeTheme()->GetSystemColor(bg_color),
       views::LayoutProvider::Get()->GetCornerRadiusMetric(
           views::EMPHASIS_HIGH)));
 }
diff --git a/chrome/browser/ui/views/storage/storage_pressure_bubble_view.cc b/chrome/browser/ui/views/storage/storage_pressure_bubble_view.cc
index bbaf274..6f39790 100644
--- a/chrome/browser/ui/views/storage/storage_pressure_bubble_view.cc
+++ b/chrome/browser/ui/views/storage/storage_pressure_bubble_view.cc
@@ -12,14 +12,15 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/url_formatter/elide_url.h"
+#include "components/url_formatter/url_formatter.h"
 #include "content/public/common/content_features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/views/layout/box_layout.h"
 
 namespace {
 
-const char kAllSitesContentSettingsUrl[] = "chrome://settings/content/all";
+const char kAllSitesContentSettingsUrl[] =
+    "chrome://settings/content/all?sort=data-stored";
 
 }  // namespace
 
@@ -90,9 +91,14 @@
       provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL)));
 
   // Description text label.
+  auto origin_string = url_formatter::FormatUrl(
+      origin_.GetURL(),
+      url_formatter::kFormatUrlOmitDefaults |
+          url_formatter::kFormatUrlOmitHTTPS |
+          url_formatter::kFormatUrlOmitTrailingSlashOnBareHostname,
+      net::UnescapeRule::NONE, nullptr, nullptr, nullptr);
   auto text_label = std::make_unique<views::Label>(l10n_util::GetStringFUTF16(
-      IDS_SETTINGS_STORAGE_PRESSURE_BUBBLE_VIEW_MESSAGE,
-      url_formatter::FormatOriginForSecurityDisplay(origin_)));
+      IDS_SETTINGS_STORAGE_PRESSURE_BUBBLE_VIEW_MESSAGE, origin_string));
   text_label->SetMultiLine(true);
   text_label->SetLineHeight(20);
   text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
diff --git a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc
index b9257f19..ca2d541 100644
--- a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.cc
@@ -51,9 +51,11 @@
 views::Widget* TabGroupEditorBubbleView::Show(
     const Browser* browser,
     const tab_groups::TabGroupId& group,
-    TabGroupHeader* anchor_view) {
+    TabGroupHeader* anchor_view,
+    bool stop_context_menu_propagation) {
   views::Widget* const widget = BubbleDialogDelegateView::CreateBubble(
-      new TabGroupEditorBubbleView(browser, group, anchor_view, base::nullopt));
+      new TabGroupEditorBubbleView(browser, group, anchor_view, base::nullopt,
+                                   stop_context_menu_propagation));
   widget->Show();
   return widget;
 }
@@ -63,8 +65,9 @@
     const Browser* browser,
     const tab_groups::TabGroupId& group,
     gfx::Rect anchor_rect) {
-  views::Widget* const widget = BubbleDialogDelegateView::CreateBubble(
-      new TabGroupEditorBubbleView(browser, group, nullptr, anchor_rect));
+  views::Widget* const widget =
+      BubbleDialogDelegateView::CreateBubble(new TabGroupEditorBubbleView(
+          browser, group, nullptr, anchor_rect, false));
   widget->Show();
   return widget;
 }
@@ -87,7 +90,8 @@
     const Browser* browser,
     const tab_groups::TabGroupId& group,
     TabGroupHeader* anchor_view,
-    base::Optional<gfx::Rect> anchor_rect)
+    base::Optional<gfx::Rect> anchor_rect,
+    bool stop_context_menu_propagation)
     : browser_(browser),
       group_(group),
       title_field_controller_(this),
@@ -146,8 +150,8 @@
   title_field_container->SetBorder(views::CreateEmptyBorder(gfx::Insets(
       0, color_element_insets.left(), vertical_dialog_content_spacing,
       color_element_insets.right())));
-  title_field_ =
-      title_field_container->AddChildView(std::make_unique<views::Textfield>());
+  title_field_ = title_field_container->AddChildView(
+      std::make_unique<TitleField>(stop_context_menu_propagation));
   title_field_->SetText(title);
   title_field_->SetAccessibleName(base::ASCIIToUTF16("Group title"));
   title_field_->SetPlaceholderText(
@@ -310,6 +314,21 @@
   return false;
 }
 
+void TabGroupEditorBubbleView::TitleField::ShowContextMenu(
+    const gfx::Point& p,
+    ui::MenuSourceType source_type) {
+  // There is no easy way to stop the propagation of a ShowContextMenu event,
+  // which is sometimes used to open the bubble itself. So when the bubble is
+  // opened this way, we manually hide the textfield's context menu the first
+  // time. Otherwise, the textfield, which is automatically focused, would show
+  // an extra context menu when the bubble first opens.
+  if (stop_context_menu_propagation_) {
+    stop_context_menu_propagation_ = false;
+    return;
+  }
+  views::Textfield::ShowContextMenu(p, source_type);
+}
+
 TabGroupEditorBubbleView::ButtonListener::ButtonListener(
     const Browser* browser,
     tab_groups::TabGroupId group,
diff --git a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h
index d701fde4..54aaeec5 100644
--- a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h
+++ b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h
@@ -40,7 +40,8 @@
   // Returns an *unowned* pointer to the bubble's widget.
   static views::Widget* Show(const Browser* browser,
                              const tab_groups::TabGroupId& group,
-                             TabGroupHeader* anchor_view);
+                             TabGroupHeader* anchor_view,
+                             bool stop_context_menu_propagation = false);
 
   // Shows the editor for |group| using a rect as an anchor. Should only be used
   // if the TabGroupHeader is not available as an anchor, e.g. in WebUI. Returns
@@ -58,7 +59,8 @@
   TabGroupEditorBubbleView(const Browser* browser,
                            const tab_groups::TabGroupId& group,
                            TabGroupHeader* anchor_view,
-                           base::Optional<gfx::Rect> anchor_rect);
+                           base::Optional<gfx::Rect> anchor_rect,
+                           bool stop_context_menu_propagation);
   ~TabGroupEditorBubbleView() override;
 
   void UpdateGroup();
@@ -88,6 +90,26 @@
 
   TitleFieldController title_field_controller_;
 
+  class TitleField : public views::Textfield {
+   public:
+    explicit TitleField(bool stop_context_menu_propagation)
+        : stop_context_menu_propagation_(stop_context_menu_propagation) {}
+    ~TitleField() override = default;
+
+    // views::Textfield:
+    void ShowContextMenu(const gfx::Point& p,
+                         ui::MenuSourceType source_type) override;
+
+   private:
+    // Whether the context menu should be hidden the first time it shows.
+    // Needed because there is no easy way to stop the propagation of a
+    // ShowContextMenu event, which is sometimes used to open the bubble
+    // itself.
+    bool stop_context_menu_propagation_;
+  };
+
+  TitleField* title_field_;
+
   class ButtonListener : public views::ButtonListener {
    public:
     explicit ButtonListener(const Browser* browser,
@@ -105,8 +127,6 @@
 
   ButtonListener button_listener_;
 
-  views::Textfield* title_field_;
-
   std::vector<tab_groups::TabGroupColorId> color_ids_;
   std::vector<std::pair<SkColor, base::string16>> colors_;
   ColorPickerView* color_selector_;
diff --git a/chrome/browser/ui/views/tabs/tab_group_header.cc b/chrome/browser/ui/views/tabs/tab_group_header.cc
index dd5fe02a..8ca9228 100644
--- a/chrome/browser/ui/views/tabs/tab_group_header.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_header.cc
@@ -78,6 +78,7 @@
   DCHECK(tab_strip);
 
   set_group(group);
+  set_context_menu_controller(this);
 
   // The size and color of the chip are set in VisualsChanged().
   title_chip_ = AddChildView(std::make_unique<views::View>());
@@ -227,6 +228,17 @@
   return size_info;
 }
 
+void TabGroupHeader::ShowContextMenuForViewImpl(
+    views::View* source,
+    const gfx::Point& point,
+    ui::MenuSourceType source_type) {
+  if (editor_bubble_tracker_.is_open())
+    return;
+
+  editor_bubble_tracker_.Opened(TabGroupEditorBubbleView::Show(
+      tab_strip_->controller()->GetBrowser(), group().value(), this, true));
+}
+
 int TabGroupHeader::CalculateWidth() const {
   // We don't want tabs to visually overlap group headers, so we add that space
   // to the width to compensate. We don't want to actually remove the overlap
diff --git a/chrome/browser/ui/views/tabs/tab_group_header.h b/chrome/browser/ui/views/tabs/tab_group_header.h
index 29fd2870..32e6ba77 100644
--- a/chrome/browser/ui/views/tabs/tab_group_header.h
+++ b/chrome/browser/ui/views/tabs/tab_group_header.h
@@ -7,6 +7,7 @@
 
 #include "chrome/browser/ui/views/tabs/tab_slot_view.h"
 #include "components/tab_groups/tab_group_id.h"
+#include "ui/views/context_menu_controller.h"
 #include "ui/views/controls/focus_ring.h"
 #include "ui/views/widget/widget_observer.h"
 
@@ -21,7 +22,7 @@
 // View for tab group headers in the tab strip, which are markers of group
 // boundaries. There is one header for each group, which is included in the tab
 // strip flow and positioned left of the leftmost tab in the group.
-class TabGroupHeader : public TabSlotView {
+class TabGroupHeader : public TabSlotView, public views::ContextMenuController {
  public:
   TabGroupHeader(TabStrip* tab_strip, const tab_groups::TabGroupId& group);
   ~TabGroupHeader() override;
@@ -39,6 +40,11 @@
   TabSlotView::ViewType GetTabSlotViewType() const override;
   TabSizeInfo GetTabSizeInfo() const override;
 
+  // views::ContextMenuController:
+  void ShowContextMenuForViewImpl(views::View* source,
+                                  const gfx::Point& point,
+                                  ui::MenuSourceType source_type) override;
+
   // Updates our visual state according to the tab_groups::TabGroupVisualData
   // for our group.
   void VisualsChanged();
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 8a8107b5..ab24fc2 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -66,6 +66,9 @@
 // frequency it has been ignored.
 const char kImportantSitesDialogHistory[] = "important_sites_dialog";
 
+// This is the profile creation time.
+const char kProfileCreationTime[] = "profile.creation_time";
+
 #if defined(OS_WIN)
 // This is a timestamp of the last time this profile was reset by a third party
 // tool. On Windows, a third party tool may set a registry value that will be
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 899fde4..2e0e9ac 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -31,6 +31,7 @@
 extern const char kHomePageIsNewTabPage[];
 extern const char kHomePage[];
 extern const char kImportantSitesDialogHistory[];
+extern const char kProfileCreationTime[];
 #if defined(OS_WIN)
 extern const char kLastProfileResetTimestamp[];
 extern const char kChromeCleanerResetPending[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index d1ca426..d7bd45c8 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5036,6 +5036,7 @@
       "../browser/safe_browsing/local_two_phase_testserver.h",
       "../browser/safe_browsing/safe_browsing_navigation_observer_unittest.cc",
       "../browser/safe_browsing/signature_evaluator_mac_unittest.cc",
+      "../browser/safe_browsing/verdict_cache_manager_factory_unittest.cc",
       "../common/safe_browsing/binary_feature_extractor_mac_unittest.cc",
       "../common/safe_browsing/binary_feature_extractor_unittest.cc",
       "../common/safe_browsing/binary_feature_extractor_win_unittest.cc",
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 5710f7ed..4a90a3c4 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -212,6 +212,7 @@
     "$root_gen_dir/chrome/test/data/webui/settings/appearance_page_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/appearance_fonts_page_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/checkbox_tests.m.js",
+    "$root_gen_dir/chrome/test/data/webui/settings/clear_browsing_data_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/controlled_button_tests.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/controlled_radio_button_tests.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/default_browser_browsertest.m.js",
@@ -246,6 +247,7 @@
     "$root_gen_dir/chrome/test/data/webui/settings/test_lifetime_browser_proxy.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/test_reset_browser_proxy.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/test_search_engines_browser_proxy.m.js",
+    "$root_gen_dir/chrome/test/data/webui/settings/test_sync_browser_proxy.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/test_util.m.js",
     "$root_gen_dir/chrome/test/data/webui/test_browser_proxy.m.js",
     "$root_gen_dir/chrome/test/data/webui/test_store.m.js",
diff --git a/chrome/test/data/webui/mocha_adapter.js b/chrome/test/data/webui/mocha_adapter.js
index 504e8ca..cfca41c 100644
--- a/chrome/test/data/webui/mocha_adapter.js
+++ b/chrome/test/data/webui/mocha_adapter.js
@@ -77,6 +77,14 @@
   mocha.grep(new RegExp('^' + suiteName + ' ' + escapedTestName + '$')).run();
 };
 
+/**
+ * Helper function provided to make running a single Mocha suite more robust.
+ * @param {string} suiteName
+ */
+window.runMochaSuite = function(suiteName) {
+  mocha.grep(new RegExp('^' + suiteName + ' ')).run();
+};
+
 // Configure mocha.
 mocha.setup({
   // Use TDD interface instead of BDD.
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index 12d5ce6..4a172eb9 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -12,6 +12,7 @@
     "appearance_fonts_page_test.js",
     "appearance_page_test.js",
     "checkbox_tests.js",
+    "clear_browsing_data_test.js",
     "controlled_button_tests.js",
     "controlled_radio_button_tests.js",
     "default_browser_browsertest.js",
@@ -47,6 +48,7 @@
     "test_lifetime_browser_proxy.js",
     "test_reset_browser_proxy.js",
     "test_search_engines_browser_proxy.js",
+    "test_sync_browser_proxy.js",
     "test_util.js",
   ]
   if (is_win) {
diff --git a/chrome/test/data/webui/settings/clear_browsing_data_test.js b/chrome/test/data/webui/settings/clear_browsing_data_test.js
new file mode 100644
index 0000000..f6001848c
--- /dev/null
+++ b/chrome/test/data/webui/settings/clear_browsing_data_test.js
@@ -0,0 +1,583 @@
+// 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.
+
+// clang-format off
+// #import {ClearBrowsingDataBrowserProxyImpl, Router, routes, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
+// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+// #import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+// #import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
+// #import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
+// #import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+// #import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+// #import {isChildVisible, isVisible, whenAttributeIs} from 'chrome://test/test_util.m.js';
+// clang-format on
+
+cr.define('settings_clear_browsing_data_test', function() {
+  /** @implements {settings.ClearBrowsingDataBrowserProxy} */
+  class TestClearBrowsingDataBrowserProxy extends TestBrowserProxy {
+    constructor() {
+      super(['initialize', 'clearBrowsingData', 'getInstalledApps']);
+
+      /**
+       * The promise to return from |clearBrowsingData|.
+       * Allows testing code to test what happens after the call is made, and
+       * before the browser responds.
+       * @private {?Promise}
+       */
+      this.clearBrowsingDataPromise_ = null;
+
+      /**
+       * Response for |getInstalledApps|.
+       * @private {!Array<!InstalledApp>}
+       */
+      this.installedApps_ = [];
+    }
+
+    /** @param {!Promise} promise */
+    setClearBrowsingDataPromise(promise) {
+      this.clearBrowsingDataPromise_ = promise;
+    }
+
+    /** @override */
+    clearBrowsingData(dataTypes, timePeriod, installedApps) {
+      this.methodCalled(
+          'clearBrowsingData', [dataTypes, timePeriod, installedApps]);
+      cr.webUIListenerCallback('browsing-data-removing', true);
+      return this.clearBrowsingDataPromise_ !== null ?
+          this.clearBrowsingDataPromise_ :
+          Promise.resolve();
+    }
+
+    /** @param {!Array<!InstalledApp>} apps */
+    setInstalledApps(apps) {
+      this.installedApps_ = apps;
+    }
+
+    /** @override */
+    getInstalledApps(timePeriod) {
+      this.methodCalled('getInstalledApps');
+      return Promise.resolve(this.installedApps_);
+    }
+
+    /** @override */
+    initialize() {
+      this.methodCalled('initialize');
+      return Promise.resolve(false);
+    }
+  }
+
+  function getClearBrowsingDataPrefs() {
+    return {
+      browser: {
+        clear_data: {
+          browsing_history: {
+            key: 'browser.clear_data.browsing_history',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+          browsing_history_basic: {
+            key: 'browser.clear_data.browsing_history_basic',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+          cache: {
+            key: 'browser.clear_data.cache',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+          cache_basic: {
+            key: 'browser.clear_data.cache_basic',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+          cookies: {
+            key: 'browser.clear_data.cookies',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+          cookies_basic: {
+            key: 'browser.clear_data.cookies_basic',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+          download_history: {
+            key: 'browser.clear_data.download_history',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+          hosted_apps_data: {
+            key: 'browser.clear_data.hosted_apps_data',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+          form_data: {
+            key: 'browser.clear_data.form_data',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+          passwords: {
+            key: 'browser.clear_data.passwords',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+          site_settings: {
+            key: 'browser.clear_data.site_settings',
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          },
+          time_period: {
+            key: 'browser.clear_data.time_period',
+            type: chrome.settingsPrivate.PrefType.NUMBER,
+            value: 0,
+          },
+          time_period_basic: {
+            key: 'browser.clear_data.time_period_basic',
+            type: chrome.settingsPrivate.PrefType.NUMBER,
+            value: 0,
+          },
+        },
+        last_clear_browsing_data_tab: {
+          key: 'browser.last_clear_browsing_data_tab',
+          type: chrome.settingsPrivate.PrefType.NUMBER,
+          value: 0,
+        },
+      }
+    };
+  }
+
+  suite('ClearBrowsingDataDesktop', function() {
+    /** @type {settings.TestClearBrowsingDataBrowserProxy} */
+    let testBrowserProxy;
+
+    /** @type {TestSyncBrowserProxy} */
+    let testSyncBrowserProxy;
+
+    /** @type {SettingsClearBrowsingDataDialogElement} */
+    let element;
+
+    setup(function() {
+      testBrowserProxy = new TestClearBrowsingDataBrowserProxy();
+      settings.ClearBrowsingDataBrowserProxyImpl.instance_ = testBrowserProxy;
+      testSyncBrowserProxy = new TestSyncBrowserProxy();
+      settings.SyncBrowserProxyImpl.instance_ = testSyncBrowserProxy;
+      PolymerTest.clearBody();
+      element = document.createElement('settings-clear-browsing-data-dialog');
+      element.set('prefs', getClearBrowsingDataPrefs());
+      document.body.appendChild(element);
+      return testBrowserProxy.whenCalled('initialize').then(() => {
+        assertTrue(element.$$('#clearBrowsingDataDialog').open);
+      });
+    });
+
+    teardown(function() {
+      element.remove();
+    });
+
+    test('ClearBrowsingDataSyncAccountInfoDesktop', function() {
+      // Not syncing: the footer is hidden.
+      cr.webUIListenerCallback('sync-status-changed', {
+        signedIn: false,
+        hasError: false,
+      });
+      Polymer.dom.flush();
+      assertFalse(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
+
+      // Syncing: the footer is shown, with the normal sync info.
+      cr.webUIListenerCallback('sync-status-changed', {
+        signedIn: true,
+        hasError: false,
+      });
+      Polymer.dom.flush();
+      assertTrue(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
+      assertTrue(test_util.isChildVisible(element, '#sync-info'));
+      assertFalse(test_util.isChildVisible(element, '#sync-paused-info'));
+      assertFalse(
+          test_util.isChildVisible(element, '#sync-passphrase-error-info'));
+      assertFalse(test_util.isChildVisible(element, '#sync-other-error-info'));
+
+      // Sync is paused.
+      cr.webUIListenerCallback('sync-status-changed', {
+        signedIn: true,
+        hasError: true,
+        statusAction: settings.StatusAction.REAUTHENTICATE,
+      });
+      Polymer.dom.flush();
+      assertFalse(test_util.isChildVisible(element, '#sync-info'));
+      assertTrue(test_util.isChildVisible(element, '#sync-paused-info'));
+      assertFalse(
+          test_util.isChildVisible(element, '#sync-passphrase-error-info'));
+      assertFalse(test_util.isChildVisible(element, '#sync-other-error-info'));
+
+      // Sync passphrase error.
+      cr.webUIListenerCallback('sync-status-changed', {
+        signedIn: true,
+        hasError: true,
+        statusAction: settings.StatusAction.ENTER_PASSPHRASE,
+      });
+      Polymer.dom.flush();
+      assertFalse(test_util.isChildVisible(element, '#sync-info'));
+      assertFalse(test_util.isChildVisible(element, '#sync-paused-info'));
+      assertTrue(
+          test_util.isChildVisible(element, '#sync-passphrase-error-info'));
+      assertFalse(test_util.isChildVisible(element, '#sync-other-error-info'));
+
+      // Other sync error.
+      cr.webUIListenerCallback('sync-status-changed', {
+        signedIn: true,
+        hasError: true,
+        statusAction: settings.StatusAction.NO_ACTION,
+      });
+      Polymer.dom.flush();
+      assertFalse(test_util.isChildVisible(element, '#sync-info'));
+      assertFalse(test_util.isChildVisible(element, '#sync-paused-info'));
+      assertFalse(
+          test_util.isChildVisible(element, '#sync-passphrase-error-info'));
+      assertTrue(test_util.isChildVisible(element, '#sync-other-error-info'));
+    });
+
+    test('ClearBrowsingDataPauseSyncDesktop', function() {
+      cr.webUIListenerCallback('sync-status-changed', {
+        signedIn: true,
+        hasError: false,
+      });
+      Polymer.dom.flush();
+      assertTrue(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
+      const syncInfo = element.$$('#sync-info');
+      assertTrue(test_util.isVisible(syncInfo));
+      const signoutLink = syncInfo.querySelector('a[href]');
+      assertTrue(!!signoutLink);
+      assertEquals(0, testSyncBrowserProxy.getCallCount('pauseSync'));
+      signoutLink.click();
+      assertEquals(1, testSyncBrowserProxy.getCallCount('pauseSync'));
+    });
+
+    test('ClearBrowsingDataStartSignInDesktop', function() {
+      cr.webUIListenerCallback('sync-status-changed', {
+        signedIn: true,
+        hasError: true,
+        statusAction: settings.StatusAction.REAUTHENTICATE,
+      });
+      Polymer.dom.flush();
+      assertTrue(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
+      const syncInfo = element.$$('#sync-paused-info');
+      assertTrue(test_util.isVisible(syncInfo));
+      const signinLink = syncInfo.querySelector('a[href]');
+      assertTrue(!!signinLink);
+      assertEquals(0, testSyncBrowserProxy.getCallCount('startSignIn'));
+      signinLink.click();
+      assertEquals(1, testSyncBrowserProxy.getCallCount('startSignIn'));
+    });
+
+    test('ClearBrowsingDataHandlePassphraseErrorDesktop', function() {
+      cr.webUIListenerCallback('sync-status-changed', {
+        signedIn: true,
+        hasError: true,
+        statusAction: settings.StatusAction.ENTER_PASSPHRASE,
+      });
+      Polymer.dom.flush();
+      assertTrue(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
+      const syncInfo = element.$$('#sync-passphrase-error-info');
+      assertTrue(test_util.isVisible(syncInfo));
+      const passphraseLink = syncInfo.querySelector('a[href]');
+      assertTrue(!!passphraseLink);
+      passphraseLink.click();
+      assertEquals(
+          settings.routes.SYNC,
+          settings.Router.getInstance().getCurrentRoute());
+    });
+  });
+
+  suite('ClearBrowsingDataAllPlatforms', function() {
+    /** @type {settings.TestClearBrowsingDataBrowserProxy} */
+    let testBrowserProxy;
+
+    /** @type {SettingsClearBrowsingDataDialogElement} */
+    let element;
+
+    setup(function() {
+      testBrowserProxy = new TestClearBrowsingDataBrowserProxy();
+      settings.ClearBrowsingDataBrowserProxyImpl.instance_ = testBrowserProxy;
+      PolymerTest.clearBody();
+      element = document.createElement('settings-clear-browsing-data-dialog');
+      element.set('prefs', getClearBrowsingDataPrefs());
+      document.body.appendChild(element);
+      return testBrowserProxy.whenCalled('initialize');
+    });
+
+    teardown(function() {
+      element.remove();
+    });
+
+    test('ClearBrowsingDataTap', function() {
+      assertTrue(element.$$('#clearBrowsingDataDialog').open);
+      assertFalse(element.$$('#installedAppsDialog').open);
+
+      const cancelButton = element.$$('.cancel-button');
+      assertTrue(!!cancelButton);
+      const actionButton = element.$$('.action-button');
+      assertTrue(!!actionButton);
+      const spinner = element.$$('paper-spinner-lite');
+      assertTrue(!!spinner);
+
+      // Select a datatype for deletion to enable the clear button.
+      const cookieCheckbox = element.$$('#cookiesCheckboxBasic');
+      assertTrue(!!cookieCheckbox);
+      cookieCheckbox.$.checkbox.click();
+
+      assertFalse(cancelButton.disabled);
+      assertFalse(actionButton.disabled);
+      assertFalse(spinner.active);
+
+      const promiseResolver = new PromiseResolver();
+      testBrowserProxy.setClearBrowsingDataPromise(promiseResolver.promise);
+      actionButton.click();
+
+      return testBrowserProxy.whenCalled('clearBrowsingData')
+          .then(function(args) {
+            const dataTypes = args[0];
+            const timePeriod = args[1];
+            const installedApps = args[2];
+            assertEquals(1, dataTypes.length);
+            assertEquals('browser.clear_data.cookies_basic', dataTypes[0]);
+            assertTrue(element.$$('#clearBrowsingDataDialog').open);
+            assertTrue(cancelButton.disabled);
+            assertTrue(actionButton.disabled);
+            assertTrue(spinner.active);
+            assertTrue(installedApps.length == 0);
+
+            // Simulate signal from browser indicating that clearing has
+            // completed.
+            cr.webUIListenerCallback('browsing-data-removing', false);
+            promiseResolver.resolve();
+            // Yields to the message loop to allow the callback chain of the
+            // Promise that was just resolved to execute before the
+            // assertions.
+          })
+          .then(function() {
+            assertFalse(element.$$('#clearBrowsingDataDialog').open);
+            assertFalse(cancelButton.disabled);
+            assertFalse(actionButton.disabled);
+            assertFalse(spinner.active);
+            assertFalse(!!element.$$('#notice'));
+
+            // Check that the dialog didn't switch to installed apps.
+            assertFalse(element.$$('#installedAppsDialog').open);
+          });
+    });
+
+    test('ClearBrowsingDataClearButton', function() {
+      assertTrue(element.$$('#clearBrowsingDataDialog').open);
+
+      const actionButton = element.$$('.action-button');
+      assertTrue(!!actionButton);
+      const cookieCheckboxBasic = element.$$('#cookiesCheckboxBasic');
+      assertTrue(!!cookieCheckboxBasic);
+      // Initially the button is disabled because all checkboxes are off.
+      assertTrue(actionButton.disabled);
+      // The button gets enabled if any checkbox is selected.
+      cookieCheckboxBasic.$.checkbox.click();
+      assertTrue(cookieCheckboxBasic.checked);
+      assertFalse(actionButton.disabled);
+      // Switching to advanced disables the button.
+      element.$$('cr-tabs').selected = 1;
+      assertTrue(actionButton.disabled);
+      // Switching back enables it again.
+      element.$$('cr-tabs').selected = 0;
+      assertFalse(actionButton.disabled);
+    });
+
+    test('showHistoryDeletionDialog', function() {
+      assertTrue(element.$$('#clearBrowsingDataDialog').open);
+      const actionButton = element.$$('.action-button');
+      assertTrue(!!actionButton);
+
+      // Select a datatype for deletion to enable the clear button.
+      const cookieCheckbox = element.$$('#cookiesCheckboxBasic');
+      assertTrue(!!cookieCheckbox);
+      cookieCheckbox.$.checkbox.click();
+      assertFalse(actionButton.disabled);
+
+      const promiseResolver = new PromiseResolver();
+      testBrowserProxy.setClearBrowsingDataPromise(promiseResolver.promise);
+      actionButton.click();
+
+      return testBrowserProxy.whenCalled('clearBrowsingData')
+          .then(function() {
+            // Passing showNotice = true should trigger the notice about other
+            // forms of browsing history to open, and the dialog to stay open.
+            promiseResolver.resolve(true /* showNotice */);
+
+            // Yields to the message loop to allow the callback chain of the
+            // Promise that was just resolved to execute before the
+            // assertions.
+          })
+          .then(function() {
+            Polymer.dom.flush();
+            const notice = element.$$('#notice');
+            assertTrue(!!notice);
+            const noticeActionButton = notice.$$('.action-button');
+            assertTrue(!!noticeActionButton);
+
+            assertTrue(element.$$('#clearBrowsingDataDialog').open);
+            assertTrue(notice.$$('#dialog').open);
+
+            noticeActionButton.click();
+
+            return new Promise(function(resolve, reject) {
+              // Tapping the action button will close the notice. Move to the
+              // end of the message loop to allow the closing event to
+              // propagate to the parent dialog. The parent dialog should
+              // subsequently close as well.
+              setTimeout(function() {
+                const notice = element.$$('#notice');
+                assertFalse(!!notice);
+                assertFalse(element.$$('#clearBrowsingDataDialog').open);
+                resolve();
+              }, 0);
+            });
+          });
+    });
+
+    test('Counters', function() {
+      assertTrue(element.$$('#clearBrowsingDataDialog').open);
+
+      const checkbox = element.$$('#cacheCheckboxBasic');
+      assertEquals('browser.clear_data.cache_basic', checkbox.pref.key);
+
+      // Simulate a browsing data counter result for history. This checkbox's
+      // sublabel should be updated.
+      cr.webUIListenerCallback(
+          'update-counter-text', checkbox.pref.key, 'result');
+      assertEquals('result', checkbox.subLabel);
+    });
+
+    test('history rows are hidden for supervised users', function() {
+      assertFalse(loadTimeData.getBoolean('isSupervised'));
+      assertFalse(element.$$('#browsingCheckbox').hidden);
+      assertFalse(element.$$('#browsingCheckboxBasic').hidden);
+      assertFalse(element.$$('#downloadCheckbox').hidden);
+
+      element.remove();
+      testBrowserProxy.reset();
+      loadTimeData.overrideValues({isSupervised: true});
+
+      element = document.createElement('settings-clear-browsing-data-dialog');
+      document.body.appendChild(element);
+      Polymer.dom.flush();
+
+      return testBrowserProxy.whenCalled('initialize').then(function() {
+        assertTrue(element.$$('#browsingCheckbox').hidden);
+        assertTrue(element.$$('#browsingCheckboxBasic').hidden);
+        assertTrue(element.$$('#downloadCheckbox').hidden);
+      });
+    });
+
+    if (cr.isChromeOS) {
+      // On ChromeOS the footer is never shown.
+      test('ClearBrowsingDataSyncAccountInfo', function() {
+        assertTrue(element.$$('#clearBrowsingDataDialog').open);
+
+        // Not syncing.
+        cr.webUIListenerCallback('sync-status-changed', {
+          signedIn: false,
+          hasError: false,
+        });
+        Polymer.dom.flush();
+        assertFalse(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
+
+        // Syncing.
+        cr.webUIListenerCallback('sync-status-changed', {
+          signedIn: true,
+          hasError: false,
+        });
+        Polymer.dom.flush();
+        assertFalse(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
+
+        // Sync passphrase error.
+        cr.webUIListenerCallback('sync-status-changed', {
+          signedIn: true,
+          hasError: true,
+          statusAction: settings.StatusAction.ENTER_PASSPHRASE,
+        });
+        Polymer.dom.flush();
+        assertFalse(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
+
+        // Other sync error.
+        cr.webUIListenerCallback('sync-status-changed', {
+          signedIn: true,
+          hasError: true,
+          statusAction: settings.StatusAction.NO_ACTION,
+        });
+        Polymer.dom.flush();
+        assertFalse(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
+      });
+    }
+  });
+
+  suite('InstalledApps', function() {
+    /** @type {settings.TestClearBrowsingDataBrowserProxy} */
+    let testBrowserProxy;
+
+    /** @type {SettingsClearBrowsingDataDialogElement} */
+    let element;
+
+    /** @type {Array<InstalledApp>} */
+    const installedApps = [
+      {registerableDomain: 'google.com', isChecked: true},
+      {registerableDomain: 'yahoo.com', isChecked: true},
+    ];
+
+    setup(() => {
+      loadTimeData.overrideValues({installedAppsInCbd: true});
+      testBrowserProxy = new TestClearBrowsingDataBrowserProxy();
+      testBrowserProxy.setInstalledApps(installedApps);
+      settings.ClearBrowsingDataBrowserProxyImpl.instance_ = testBrowserProxy;
+      PolymerTest.clearBody();
+      element = document.createElement('settings-clear-browsing-data-dialog');
+      element.set('prefs', getClearBrowsingDataPrefs());
+      document.body.appendChild(element);
+      return testBrowserProxy.whenCalled('initialize');
+    });
+
+    teardown(() => {
+      element.remove();
+    });
+
+    test('getInstalledApps', async function() {
+      assertTrue(element.$.clearBrowsingDataDialog.open);
+      assertFalse(element.$.installedAppsDialog.open);
+
+      // Select cookie checkbox.
+      element.$.cookiesCheckboxBasic.$.checkbox.click();
+      assertTrue(element.$.cookiesCheckboxBasic.checked);
+      // Clear browsing data.
+      element.$.clearBrowsingDataConfirm.click();
+      assertTrue(element.$.clearBrowsingDataDialog.open);
+
+      await testBrowserProxy.whenCalled('getInstalledApps');
+      await test_util.whenAttributeIs(
+          element.$.installedAppsDialog, 'open', '');
+      const firstInstalledApp = element.$$('installed-app-checkbox');
+      assertTrue(!!firstInstalledApp);
+      assertEquals(
+          'google.com', firstInstalledApp.installed_app.registerableDomain);
+      assertTrue(firstInstalledApp.installed_app.isChecked);
+      // Choose to keep storage for google.com.
+      firstInstalledApp.$.checkbox.click();
+      assertFalse(firstInstalledApp.installed_app.isChecked);
+      // Confirm deletion.
+      element.$.installedAppsConfirm.click();
+      const [dataTypes, timePeriod, apps] =
+          await testBrowserProxy.whenCalled('clearBrowsingData');
+      assertEquals(1, dataTypes.length);
+      assertEquals('browser.clear_data.cookies_basic', dataTypes[0]);
+      assertEquals(2, apps.length);
+      assertEquals('google.com', apps[0].registerableDomain);
+      assertFalse(apps[0].isChecked);
+      assertEquals('yahoo.com', apps[1].registerableDomain);
+      assertTrue(apps[1].isChecked);
+    });
+  });
+  // #cr_define_end
+});
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 6c285d0..e783b42 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -933,11 +933,6 @@
   ]),
 };
 
-TEST_F('CrSettingsPrivacyPageTest', 'ClearBrowsingDataTests', function() {
-  settings_privacy_page.registerClearBrowsingDataTests();
-  mocha.run();
-});
-
 TEST_F('CrSettingsPrivacyPageTest', 'PrivacyPageTests', function() {
   settings_privacy_page.registerPrivacyPageTests();
   mocha.run();
@@ -955,11 +950,6 @@
   mocha.run();
 });
 
-TEST_F('CrSettingsPrivacyPageTest', 'InstalledAppsTests', () => {
-  settings_privacy_page.registerInstalledAppsTests();
-  mocha.run();
-});
-
 GEN('#if defined(OS_MACOSX) || defined(OS_WIN)');
 // TODO(crbug.com/1043665): disabling due to failures on several builders.
 TEST_F(
@@ -970,11 +960,46 @@
     });
 GEN('#endif');
 
+/**
+ * Test fixture for
+ * chrome/browser/resources/settings/clear_browsing_data_dialog/
+ *     clear_browsing_data_dialog.html.
+ * @constructor
+ * @extends {CrSettingsBrowserTest}
+ */
+function CrSettingsClearBrowsingDataTest() {}
+
+CrSettingsClearBrowsingDataTest.prototype = {
+  __proto__: CrSettingsBrowserTest.prototype,
+
+  /** @override */
+  browsePreload: 'chrome://settings/clear_browsing_data_dialog/' +
+      'clear_browsing_data_dialog.html',
+
+  /** @override */
+  extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
+    '//ui/webui/resources/js/promise_resolver.js',
+    '../test_util.js',
+    '../test_browser_proxy.js',
+    'clear_browsing_data_test.js',
+    'test_sync_browser_proxy.js',
+  ]),
+};
+
+TEST_F(
+    'CrSettingsClearBrowsingDataTest', 'ClearBrowsingDataAllPlatforms',
+    function() {
+      runMochaSuite('ClearBrowsingDataAllPlatforms');
+    });
+
+TEST_F('CrSettingsClearBrowsingDataTest', 'InstalledApps', () => {
+  runMochaSuite('InstalledApps');
+});
+
 GEN('#if !defined(OS_CHROMEOS)');
 TEST_F(
-    'CrSettingsPrivacyPageTest', 'ClearBrowsingDataTestsDesktop', function() {
-      settings_privacy_page.registerClearBrowsingDataTestsDesktop();
-      mocha.run();
+    'CrSettingsClearBrowsingDataTest', 'ClearBrowsingDataDesktop', function() {
+      runMochaSuite('ClearBrowsingDataDesktop');
     });
 GEN('#endif');
 
diff --git a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js b/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
index c7a9889..0a48f13 100644
--- a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
@@ -434,3 +434,29 @@
 TEST_F('CrSettingsSearchV3Test', 'All', function() {
   mocha.run();
 });
+
+// eslint-disable-next-line no-var
+var CrSettingsClearBrowsingDataV3Test = class extends CrSettingsV3BrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://settings/test_loader.html?module=settings/clear_browsing_data_test.m.js';
+  }
+};
+
+TEST_F(
+    'CrSettingsClearBrowsingDataV3Test', 'ClearBrowsingDataAllPlatforms',
+    function() {
+      runMochaSuite('ClearBrowsingDataAllPlatforms');
+    });
+
+TEST_F('CrSettingsClearBrowsingDataV3Test', 'InstalledApps', () => {
+  runMochaSuite('InstalledApps');
+});
+
+GEN('#if !defined(OS_CHROMEOS)');
+TEST_F(
+    'CrSettingsClearBrowsingDataV3Test', 'ClearBrowsingDataDesktop',
+    function() {
+      runMochaSuite('ClearBrowsingDataDesktop');
+    });
+GEN('#endif');
diff --git a/chrome/test/data/webui/settings/privacy_page_test.js b/chrome/test/data/webui/settings/privacy_page_test.js
index e01a1fd..5b46923 100644
--- a/chrome/test/data/webui/settings/privacy_page_test.js
+++ b/chrome/test/data/webui/settings/privacy_page_test.js
@@ -4,138 +4,6 @@
 
 cr.define('settings_privacy_page', function() {
 
-  /** @implements {settings.ClearBrowsingDataBrowserProxy} */
-  class TestClearBrowsingDataBrowserProxy extends TestBrowserProxy {
-    constructor() {
-      super(['initialize', 'clearBrowsingData', 'getInstalledApps']);
-
-      /**
-       * The promise to return from |clearBrowsingData|.
-       * Allows testing code to test what happens after the call is made, and
-       * before the browser responds.
-       * @private {?Promise}
-       */
-      this.clearBrowsingDataPromise_ = null;
-
-      /**
-       * Response for |getInstalledApps|.
-       * @private {!Array<!InstalledApp>}
-       */
-      this.installedApps_ = [];
-    }
-
-    /** @param {!Promise} promise */
-    setClearBrowsingDataPromise(promise) {
-      this.clearBrowsingDataPromise_ = promise;
-    }
-
-    /** @override */
-    clearBrowsingData(dataTypes, timePeriod, installedApps) {
-      this.methodCalled(
-          'clearBrowsingData', [dataTypes, timePeriod, installedApps]);
-      cr.webUIListenerCallback('browsing-data-removing', true);
-      return this.clearBrowsingDataPromise_ !== null ?
-          this.clearBrowsingDataPromise_ :
-          Promise.resolve();
-    }
-
-    /** @param {!Array<!InstalledApp>} apps */
-    setInstalledApps(apps) {
-      this.installedApps_ = apps;
-    }
-
-    /** @override */
-    getInstalledApps(timePeriod) {
-      this.methodCalled('getInstalledApps');
-      return Promise.resolve(this.installedApps_);
-    }
-
-    /** @override */
-    initialize() {
-      this.methodCalled('initialize');
-      return Promise.resolve(false);
-    }
-  }
-
-  function getClearBrowsingDataPrefs() {
-    return {
-      browser: {
-        clear_data: {
-          browsing_history: {
-            key: 'browser.clear_data.browsing_history',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          browsing_history_basic: {
-            key: 'browser.clear_data.browsing_history_basic',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          cache: {
-            key: 'browser.clear_data.cache',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          cache_basic: {
-            key: 'browser.clear_data.cache_basic',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          cookies: {
-            key: 'browser.clear_data.cookies',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          cookies_basic: {
-            key: 'browser.clear_data.cookies_basic',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          download_history: {
-            key: 'browser.clear_data.download_history',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          hosted_apps_data: {
-            key: 'browser.clear_data.hosted_apps_data',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          form_data: {
-            key: 'browser.clear_data.form_data',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          passwords: {
-            key: 'browser.clear_data.passwords',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          site_settings: {
-            key: 'browser.clear_data.site_settings',
-            type: chrome.settingsPrivate.PrefType.BOOLEAN,
-            value: false,
-          },
-          time_period: {
-            key: 'browser.clear_data.time_period',
-            type: chrome.settingsPrivate.PrefType.NUMBER,
-            value: 0,
-          },
-          time_period_basic: {
-            key: 'browser.clear_data.time_period_basic',
-            type: chrome.settingsPrivate.PrefType.NUMBER,
-            value: 0,
-          },
-        },
-        last_clear_browsing_data_tab: {
-          key: 'browser.last_clear_browsing_data_tab',
-          type: chrome.settingsPrivate.PrefType.NUMBER,
-          value: 0,
-        },
-      }
-    };
-  }
-
   function registerUMALoggingTests() {
     suite('PrivacyPageUMACheck', function() {
       /** @type {settings.TestPrivacyPageBrowserProxy} */
@@ -336,383 +204,6 @@
     });
   }
 
-  function registerClearBrowsingDataTestsDesktop() {
-    assert(!cr.isChromeOS);
-    suite('ClearBrowsingDataDesktop', function() {
-      /** @type {settings.TestClearBrowsingDataBrowserProxy} */
-      let testBrowserProxy;
-
-      /** @type {TestSyncBrowserProxy} */
-      let testSyncBrowserProxy;
-
-      /** @type {SettingsClearBrowsingDataDialogElement} */
-      let element;
-
-      setup(function() {
-        testBrowserProxy = new TestClearBrowsingDataBrowserProxy();
-        settings.ClearBrowsingDataBrowserProxyImpl.instance_ = testBrowserProxy;
-        testSyncBrowserProxy = new TestSyncBrowserProxy();
-        settings.SyncBrowserProxyImpl.instance_ = testSyncBrowserProxy;
-        PolymerTest.clearBody();
-        element = document.createElement('settings-clear-browsing-data-dialog');
-        element.set('prefs', getClearBrowsingDataPrefs());
-        document.body.appendChild(element);
-        return testBrowserProxy.whenCalled('initialize').then(() => {
-          assertTrue(element.$$('#clearBrowsingDataDialog').open);
-        });
-      });
-
-      teardown(function() {
-        element.remove();
-      });
-
-      test('ClearBrowsingDataSyncAccountInfoDesktop', function() {
-        // Not syncing: the footer is hidden.
-        cr.webUIListenerCallback('sync-status-changed', {
-          signedIn: false,
-          hasError: false,
-        });
-        Polymer.dom.flush();
-        assertFalse(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
-
-        // Syncing: the footer is shown, with the normal sync info.
-        cr.webUIListenerCallback('sync-status-changed', {
-          signedIn: true,
-          hasError: false,
-        });
-        Polymer.dom.flush();
-        assertTrue(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
-        assertTrue(test_util.isChildVisible(element, '#sync-info'));
-        assertFalse(test_util.isChildVisible(element, '#sync-paused-info'));
-        assertFalse(
-            test_util.isChildVisible(element, '#sync-passphrase-error-info'));
-        assertFalse(
-            test_util.isChildVisible(element, '#sync-other-error-info'));
-
-        // Sync is paused.
-        cr.webUIListenerCallback('sync-status-changed', {
-          signedIn: true,
-          hasError: true,
-          statusAction: settings.StatusAction.REAUTHENTICATE,
-        });
-        Polymer.dom.flush();
-        assertFalse(test_util.isChildVisible(element, '#sync-info'));
-        assertTrue(test_util.isChildVisible(element, '#sync-paused-info'));
-        assertFalse(
-            test_util.isChildVisible(element, '#sync-passphrase-error-info'));
-        assertFalse(
-            test_util.isChildVisible(element, '#sync-other-error-info'));
-
-        // Sync passphrase error.
-        cr.webUIListenerCallback('sync-status-changed', {
-          signedIn: true,
-          hasError: true,
-          statusAction: settings.StatusAction.ENTER_PASSPHRASE,
-        });
-        Polymer.dom.flush();
-        assertFalse(test_util.isChildVisible(element, '#sync-info'));
-        assertFalse(test_util.isChildVisible(element, '#sync-paused-info'));
-        assertTrue(
-            test_util.isChildVisible(element, '#sync-passphrase-error-info'));
-        assertFalse(
-            test_util.isChildVisible(element, '#sync-other-error-info'));
-
-        // Other sync error.
-        cr.webUIListenerCallback('sync-status-changed', {
-          signedIn: true,
-          hasError: true,
-          statusAction: settings.StatusAction.NO_ACTION,
-        });
-        Polymer.dom.flush();
-        assertFalse(test_util.isChildVisible(element, '#sync-info'));
-        assertFalse(test_util.isChildVisible(element, '#sync-paused-info'));
-        assertFalse(
-            test_util.isChildVisible(element, '#sync-passphrase-error-info'));
-        assertTrue(test_util.isChildVisible(element, '#sync-other-error-info'));
-      });
-
-      test('ClearBrowsingDataPauseSyncDesktop', function() {
-        cr.webUIListenerCallback('sync-status-changed', {
-          signedIn: true,
-          hasError: false,
-        });
-        Polymer.dom.flush();
-        assertTrue(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
-        const syncInfo = element.$$('#sync-info');
-        assertTrue(test_util.isVisible(syncInfo));
-        const signoutLink = syncInfo.querySelector('a[href]');
-        assertTrue(!!signoutLink);
-        assertEquals(0, testSyncBrowserProxy.getCallCount('pauseSync'));
-        signoutLink.click();
-        assertEquals(1, testSyncBrowserProxy.getCallCount('pauseSync'));
-      });
-
-      test('ClearBrowsingDataStartSignInDesktop', function() {
-        cr.webUIListenerCallback('sync-status-changed', {
-          signedIn: true,
-          hasError: true,
-          statusAction: settings.StatusAction.REAUTHENTICATE,
-        });
-        Polymer.dom.flush();
-        assertTrue(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
-        const syncInfo = element.$$('#sync-paused-info');
-        assertTrue(test_util.isVisible(syncInfo));
-        const signinLink = syncInfo.querySelector('a[href]');
-        assertTrue(!!signinLink);
-        assertEquals(0, testSyncBrowserProxy.getCallCount('startSignIn'));
-        signinLink.click();
-        assertEquals(1, testSyncBrowserProxy.getCallCount('startSignIn'));
-      });
-
-      test('ClearBrowsingDataHandlePassphraseErrorDesktop', function() {
-        cr.webUIListenerCallback('sync-status-changed', {
-          signedIn: true,
-          hasError: true,
-          statusAction: settings.StatusAction.ENTER_PASSPHRASE,
-        });
-        Polymer.dom.flush();
-        assertTrue(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
-        const syncInfo = element.$$('#sync-passphrase-error-info');
-        assertTrue(test_util.isVisible(syncInfo));
-        const passphraseLink = syncInfo.querySelector('a[href]');
-        assertTrue(!!passphraseLink);
-        passphraseLink.click();
-        assertEquals(
-            settings.routes.SYNC,
-            settings.Router.getInstance().getCurrentRoute());
-      });
-    });
-  }
-
-  function registerClearBrowsingDataTests() {
-    suite('ClearBrowsingData', function() {
-      /** @type {settings.TestClearBrowsingDataBrowserProxy} */
-      let testBrowserProxy;
-
-      /** @type {SettingsClearBrowsingDataDialogElement} */
-      let element;
-
-      setup(function() {
-        testBrowserProxy = new TestClearBrowsingDataBrowserProxy();
-        settings.ClearBrowsingDataBrowserProxyImpl.instance_ = testBrowserProxy;
-        PolymerTest.clearBody();
-        element = document.createElement('settings-clear-browsing-data-dialog');
-        element.set('prefs', getClearBrowsingDataPrefs());
-        document.body.appendChild(element);
-        return testBrowserProxy.whenCalled('initialize');
-      });
-
-      teardown(function() {
-        element.remove();
-      });
-
-      test('ClearBrowsingDataTap', function() {
-        assertTrue(element.$$('#clearBrowsingDataDialog').open);
-        assertFalse(element.$$('#installedAppsDialog').open);
-
-        const cancelButton = element.$$('.cancel-button');
-        assertTrue(!!cancelButton);
-        const actionButton = element.$$('.action-button');
-        assertTrue(!!actionButton);
-        const spinner = element.$$('paper-spinner-lite');
-        assertTrue(!!spinner);
-
-        // Select a datatype for deletion to enable the clear button.
-        const cookieCheckbox = element.$$('#cookiesCheckboxBasic');
-        assertTrue(!!cookieCheckbox);
-        cookieCheckbox.$.checkbox.click();
-
-        assertFalse(cancelButton.disabled);
-        assertFalse(actionButton.disabled);
-        assertFalse(spinner.active);
-
-        const promiseResolver = new PromiseResolver();
-        testBrowserProxy.setClearBrowsingDataPromise(promiseResolver.promise);
-        actionButton.click();
-
-        return testBrowserProxy.whenCalled('clearBrowsingData')
-            .then(function(args) {
-              const dataTypes = args[0];
-              const timePeriod = args[1];
-              const installedApps = args[2];
-              assertEquals(1, dataTypes.length);
-              assertEquals('browser.clear_data.cookies_basic', dataTypes[0]);
-              assertTrue(element.$$('#clearBrowsingDataDialog').open);
-              assertTrue(cancelButton.disabled);
-              assertTrue(actionButton.disabled);
-              assertTrue(spinner.active);
-              assertTrue(installedApps.length == 0);
-
-              // Simulate signal from browser indicating that clearing has
-              // completed.
-              cr.webUIListenerCallback('browsing-data-removing', false);
-              promiseResolver.resolve();
-              // Yields to the message loop to allow the callback chain of the
-              // Promise that was just resolved to execute before the
-              // assertions.
-            })
-            .then(function() {
-              assertFalse(element.$$('#clearBrowsingDataDialog').open);
-              assertFalse(cancelButton.disabled);
-              assertFalse(actionButton.disabled);
-              assertFalse(spinner.active);
-              assertFalse(!!element.$$('#notice'));
-
-              // Check that the dialog didn't switch to installed apps.
-              assertFalse(element.$$('#installedAppsDialog').open);
-            });
-      });
-
-      test('ClearBrowsingDataClearButton', function() {
-        assertTrue(element.$$('#clearBrowsingDataDialog').open);
-
-        const actionButton = element.$$('.action-button');
-        assertTrue(!!actionButton);
-        const cookieCheckboxBasic = element.$$('#cookiesCheckboxBasic');
-        assertTrue(!!cookieCheckboxBasic);
-        // Initially the button is disabled because all checkboxes are off.
-        assertTrue(actionButton.disabled);
-        // The button gets enabled if any checkbox is selected.
-        cookieCheckboxBasic.$.checkbox.click();
-        assertTrue(cookieCheckboxBasic.checked);
-        assertFalse(actionButton.disabled);
-        // Switching to advanced disables the button.
-        element.$$('cr-tabs').selected = 1;
-        assertTrue(actionButton.disabled);
-        // Switching back enables it again.
-        element.$$('cr-tabs').selected = 0;
-        assertFalse(actionButton.disabled);
-      });
-
-      test('showHistoryDeletionDialog', function() {
-        assertTrue(element.$$('#clearBrowsingDataDialog').open);
-        const actionButton = element.$$('.action-button');
-        assertTrue(!!actionButton);
-
-        // Select a datatype for deletion to enable the clear button.
-        const cookieCheckbox = element.$$('#cookiesCheckboxBasic');
-        assertTrue(!!cookieCheckbox);
-        cookieCheckbox.$.checkbox.click();
-        assertFalse(actionButton.disabled);
-
-        const promiseResolver = new PromiseResolver();
-        testBrowserProxy.setClearBrowsingDataPromise(promiseResolver.promise);
-        actionButton.click();
-
-        return testBrowserProxy.whenCalled('clearBrowsingData')
-            .then(function() {
-              // Passing showNotice = true should trigger the notice about other
-              // forms of browsing history to open, and the dialog to stay open.
-              promiseResolver.resolve(true /* showNotice */);
-
-              // Yields to the message loop to allow the callback chain of the
-              // Promise that was just resolved to execute before the
-              // assertions.
-            })
-            .then(function() {
-              Polymer.dom.flush();
-              const notice = element.$$('#notice');
-              assertTrue(!!notice);
-              const noticeActionButton = notice.$$('.action-button');
-              assertTrue(!!noticeActionButton);
-
-              assertTrue(element.$$('#clearBrowsingDataDialog').open);
-              assertTrue(notice.$$('#dialog').open);
-
-              noticeActionButton.click();
-
-              return new Promise(function(resolve, reject) {
-                // Tapping the action button will close the notice. Move to the
-                // end of the message loop to allow the closing event to
-                // propagate to the parent dialog. The parent dialog should
-                // subsequently close as well.
-                setTimeout(function() {
-                  const notice = element.$$('#notice');
-                  assertFalse(!!notice);
-                  assertFalse(element.$$('#clearBrowsingDataDialog').open);
-                  resolve();
-                }, 0);
-              });
-            });
-      });
-
-      test('Counters', function() {
-        assertTrue(element.$$('#clearBrowsingDataDialog').open);
-
-        const checkbox = element.$$('#cacheCheckboxBasic');
-        assertEquals('browser.clear_data.cache_basic', checkbox.pref.key);
-
-        // Simulate a browsing data counter result for history. This checkbox's
-        // sublabel should be updated.
-        cr.webUIListenerCallback(
-            'update-counter-text', checkbox.pref.key, 'result');
-        assertEquals('result', checkbox.subLabel);
-      });
-
-      test('history rows are hidden for supervised users', function() {
-        assertFalse(loadTimeData.getBoolean('isSupervised'));
-        assertFalse(element.$$('#browsingCheckbox').hidden);
-        assertFalse(element.$$('#browsingCheckboxBasic').hidden);
-        assertFalse(element.$$('#downloadCheckbox').hidden);
-
-        element.remove();
-        testBrowserProxy.reset();
-        loadTimeData.overrideValues({isSupervised: true});
-
-        element = document.createElement('settings-clear-browsing-data-dialog');
-        document.body.appendChild(element);
-        Polymer.dom.flush();
-
-        return testBrowserProxy.whenCalled('initialize').then(function() {
-          assertTrue(element.$$('#browsingCheckbox').hidden);
-          assertTrue(element.$$('#browsingCheckboxBasic').hidden);
-          assertTrue(element.$$('#downloadCheckbox').hidden);
-        });
-      });
-
-      if (cr.isChromeOS) {
-        // On ChromeOS the footer is never shown.
-        test('ClearBrowsingDataSyncAccountInfo', function() {
-          assertTrue(element.$$('#clearBrowsingDataDialog').open);
-
-          // Not syncing.
-          cr.webUIListenerCallback('sync-status-changed', {
-            signedIn: false,
-            hasError: false,
-          });
-          Polymer.dom.flush();
-          assertFalse(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
-
-          // Syncing.
-          cr.webUIListenerCallback('sync-status-changed', {
-            signedIn: true,
-            hasError: false,
-          });
-          Polymer.dom.flush();
-          assertFalse(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
-
-          // Sync passphrase error.
-          cr.webUIListenerCallback('sync-status-changed', {
-            signedIn: true,
-            hasError: true,
-            statusAction: settings.StatusAction.ENTER_PASSPHRASE,
-          });
-          Polymer.dom.flush();
-          assertFalse(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
-
-          // Other sync error.
-          cr.webUIListenerCallback('sync-status-changed', {
-            signedIn: true,
-            hasError: true,
-            statusAction: settings.StatusAction.NO_ACTION,
-          });
-          Polymer.dom.flush();
-          assertFalse(!!element.$$('#clearBrowsingDataDialog [slot=footer]'));
-        });
-      }
-    });
-  }
-
   function registerPrivacyPageSoundTests() {
     suite('PrivacyPageSound', function() {
       /** @type {settings.TestPrivacyPageBrowserProxy} */
@@ -831,78 +322,8 @@
     });
   }
 
-  function registerInstalledAppsTests() {
-    suite('InstalledApps', function() {
-      /** @type {settings.TestClearBrowsingDataBrowserProxy} */
-      let testBrowserProxy;
-
-      /** @type {SettingsClearBrowsingDataDialogElement} */
-      let element;
-
-      /** @type {Array<InstalledApp>} */
-      const installedApps = [
-        {registerableDomain: 'google.com', isChecked: true},
-        {registerableDomain: 'yahoo.com', isChecked: true},
-      ];
-
-      setup(() => {
-        loadTimeData.overrideValues({installedAppsInCbd: true});
-        testBrowserProxy = new TestClearBrowsingDataBrowserProxy();
-        testBrowserProxy.setInstalledApps(installedApps);
-        settings.ClearBrowsingDataBrowserProxyImpl.instance_ = testBrowserProxy;
-        PolymerTest.clearBody();
-        element = document.createElement('settings-clear-browsing-data-dialog');
-        element.set('prefs', getClearBrowsingDataPrefs());
-        document.body.appendChild(element);
-        return testBrowserProxy.whenCalled('initialize');
-      });
-
-      teardown(() => {
-        element.remove();
-      });
-
-      test('getInstalledApps', async function() {
-        assertTrue(element.$.clearBrowsingDataDialog.open);
-        assertFalse(element.$.installedAppsDialog.open);
-
-        // Select cookie checkbox.
-        element.$.cookiesCheckboxBasic.$.checkbox.click();
-        assertTrue(element.$.cookiesCheckboxBasic.checked);
-        // Clear browsing data.
-        element.$.clearBrowsingDataConfirm.click();
-        assertTrue(element.$.clearBrowsingDataDialog.open);
-
-        await testBrowserProxy.whenCalled('getInstalledApps');
-        await test_util.whenAttributeIs(
-            element.$.installedAppsDialog, 'open', '');
-        const firstInstalledApp = element.$$('installed-app-checkbox');
-        assertTrue(!!firstInstalledApp);
-        assertEquals(
-            'google.com', firstInstalledApp.installed_app.registerableDomain);
-        assertTrue(firstInstalledApp.installed_app.isChecked);
-        // Choose to keep storage for google.com.
-        firstInstalledApp.$.checkbox.click();
-        assertFalse(firstInstalledApp.installed_app.isChecked);
-        // Confirm deletion.
-        element.$.installedAppsConfirm.click();
-        const [dataTypes, timePeriod, apps] =
-            await testBrowserProxy.whenCalled('clearBrowsingData');
-        assertEquals(1, dataTypes.length);
-        assertEquals('browser.clear_data.cookies_basic', dataTypes[0]);
-        assertEquals(2, apps.length);
-        assertEquals('google.com', apps[0].registerableDomain);
-        assertFalse(apps[0].isChecked);
-        assertEquals('yahoo.com', apps[1].registerableDomain);
-        assertTrue(apps[1].isChecked);
-      });
-    });
-  }
-
   return {
     registerNativeCertificateManagerTests,
-    registerClearBrowsingDataTestsDesktop,
-    registerClearBrowsingDataTests,
-    registerInstalledAppsTests,
     registerPrivacyPageTests,
     registerPrivacyPageSoundTests,
     registerUMALoggingTests,
diff --git a/chrome/test/data/webui/settings/test_sync_browser_proxy.js b/chrome/test/data/webui/settings/test_sync_browser_proxy.js
index 1012764..05e4e898 100644
--- a/chrome/test/data/webui/settings/test_sync_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_sync_browser_proxy.js
@@ -2,8 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// #import {PageStatus} from 'chrome://settings/settings.js';
+// #import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
+// #import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+
 /** @implements {settings.SyncBrowserProxy} */
-class TestSyncBrowserProxy extends TestBrowserProxy {
+/* #export */ class TestSyncBrowserProxy extends TestBrowserProxy {
   constructor() {
     const methodNames = [
       'didNavigateAwayFromSyncPage',
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index 47a3e9d..853c2b0 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -57,6 +57,7 @@
     deps = [
       ":version_header",
       "//base",
+      "//components/crash/core/common:crash_key_lib",
       "//components/prefs",
       "//components/update_client",
       "//courgette:bsdiff",
@@ -70,11 +71,16 @@
 
   source_set("lib") {
     sources = [
+      "app/app.cc",
+      "app/app.h",
+      "app/app_uninstall.cc",
+      "app/app_uninstall.h",
+      "app/app_update_all.cc",
+      "app/app_update_all.h",
       "configurator.cc",
       "configurator.h",
       "installer.cc",
       "installer.h",
-      "update_apps.cc",
       "update_apps.h",
       "update_service_in_process.cc",
       "update_service_in_process.h",
@@ -85,6 +91,10 @@
     if (is_mac) {
       sources += [
         "installer_mac.cc",
+        "server/mac/server.h",
+        "server/mac/server.mm",
+        "server/mac/service_delegate.h",
+        "server/mac/service_delegate.mm",
         "update_apps_mac.mm",
       ]
     }
@@ -92,6 +102,10 @@
     if (is_win) {
       sources += [
         "installer_win.cc",
+        "server/win/server.cc",
+        "server/win/server.h",
+        "server/win/service_main.cc",
+        "server/win/service_main.h",
         "update_apps_win.cc",
       ]
     }
@@ -100,7 +114,6 @@
       ":base",
       ":version_header",
       "//base",
-      "//chrome/updater/server",
       "//components/crash/core/common:crash_key",
       "//components/crx_file:crx_file",
       "//components/prefs",
@@ -110,7 +123,11 @@
     ]
 
     if (is_win) {
-      deps += [ "//chrome/updater/win:lib" ]
+      deps += [
+        "//chrome/updater/server/win:updater_idl_idl",
+        "//chrome/updater/win:constants",
+        "//chrome/updater/win:lib",
+      ]
     }
 
     if (is_mac) {
@@ -119,8 +136,14 @@
         "//chrome/updater/mac:network_fetcher_sources",
         "//chrome/updater/mac:update_service_client_sources",
         "//chrome/updater/mac:updater_setup_sources",
+        "//chrome/updater/server/mac:protocol",
       ]
     }
+
+    if (is_win) {
+      configs -= [ "//build/config/win:winver" ]
+      configs += [ "//chrome/updater/server/win:winver" ]
+    }
   }
 
   process_version("version_header") {
diff --git a/chrome/updater/app/app.cc b/chrome/updater/app/app.cc
new file mode 100644
index 0000000..a6f89011
--- /dev/null
+++ b/chrome/updater/app/app.cc
@@ -0,0 +1,61 @@
+// 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/updater/app/app.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/message_loop/message_pump_type.h"
+#include "base/run_loop.h"
+#include "base/task/single_thread_task_executor.h"
+#include "base/task/thread_pool/thread_pool_instance.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/updater/crash_client.h"
+#include "chrome/updater/crash_reporter.h"
+#include "chrome/updater/updater_version.h"
+#include "components/crash/core/common/crash_key.h"
+
+namespace updater {
+
+App::App() = default;
+App::~App() = default;
+
+int App::Run() {
+  crash_reporter::InitializeCrashKeys();
+  static crash_reporter::CrashKeyString<16> crash_key_process_type(
+      "process_type");
+  crash_key_process_type.Set("updater");
+  if (CrashClient::GetInstance()->InitializeCrashReporting())
+    VLOG(1) << "Crash reporting initialized.";
+  else
+    VLOG(1) << "Crash reporting is not available.";
+  StartCrashReporter(UPDATER_VERSION_STRING);
+
+  base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Updater");
+  base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
+  Initialize();
+  base::RunLoop runloop;
+  base::ScopedDisallowBlocking no_blocking_allowed_on_ui_thread;
+  int exit_code = 0;
+  quit_ = base::BindOnce(
+      [](base::OnceClosure quit, int* exit_code_out, int exit_code) {
+        *exit_code_out = exit_code;
+        std::move(quit).Run();
+      },
+      runloop.QuitWhenIdleClosure(), &exit_code);
+  FirstTaskRun();
+  runloop.Run();
+  base::ThreadPoolInstance::Get()->Shutdown();
+  return exit_code;
+}
+
+void App::Shutdown(int exit_code) {
+  std::move(quit_).Run(exit_code);
+}
+
+void App::Initialize() {}
+
+}  // namespace updater
diff --git a/chrome/updater/app/app.h b/chrome/updater/app/app.h
new file mode 100644
index 0000000..dc78a9fc
--- /dev/null
+++ b/chrome/updater/app/app.h
@@ -0,0 +1,47 @@
+// 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_UPDATER_APP_APP_H_
+#define CHROME_UPDATER_APP_APP_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+
+namespace updater {
+
+// An App is a main processing mode for the updater.
+class App : public base::RefCountedThreadSafe<App> {
+ public:
+  // Starts the thread pool and task executor, then runs a runloop on the main
+  // sequence until Shutdown() is called. Returns the exit code for the
+  // program.
+  int Run();
+
+ protected:
+  friend class base::RefCountedThreadSafe<App>;
+  App();
+  virtual ~App();
+
+  // Triggers program shutdown. Must be called on the main sequence. The program
+  // will exit with the specified code.
+  void Shutdown(int exitCode);
+
+ private:
+  // Implementations of App can override this to perform work on the main
+  // sequence while blocking is still allowed. The default implementation does
+  // nothing.
+  virtual void Initialize();
+
+  // Concrete implementations of App can execute their first task in this
+  // method. It is called on the main sequence. Blocking is not allowed. It may
+  // call Shutdown.
+  virtual void FirstTaskRun() = 0;
+
+  // A callback that quits the main sequence runloop.
+  base::OnceCallback<void(int)> quit_;
+};
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_APP_APP_H_
diff --git a/chrome/updater/app/app_uninstall.cc b/chrome/updater/app/app_uninstall.cc
new file mode 100644
index 0000000..42699ee1
--- /dev/null
+++ b/chrome/updater/app/app_uninstall.cc
@@ -0,0 +1,40 @@
+// 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/updater/app/app_uninstall.h"
+
+#include "base/bind.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "build/build_config.h"
+#include "chrome/updater/app/app.h"
+
+#if defined(OS_WIN)
+#include "chrome/updater/win/setup/uninstall.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "chrome/updater/mac/setup/setup.h"
+#endif
+
+namespace updater {
+
+// AppUninstall uninstalls the updater.
+class AppUninstall : public App {
+ private:
+  ~AppUninstall() override = default;
+  void FirstTaskRun() override;
+};
+
+void AppUninstall::FirstTaskRun() {
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock()}, base::BindOnce(&Uninstall, false),
+      base::BindOnce(&AppUninstall::Shutdown, this));
+}
+
+scoped_refptr<App> MakeAppUninstall() {
+  return base::MakeRefCounted<AppUninstall>();
+}
+
+}  // namespace updater
diff --git a/chrome/updater/app/app_uninstall.h b/chrome/updater/app/app_uninstall.h
new file mode 100644
index 0000000..79b16c3
--- /dev/null
+++ b/chrome/updater/app/app_uninstall.h
@@ -0,0 +1,18 @@
+// 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_UPDATER_APP_APP_UNINSTALL_H_
+#define CHROME_UPDATER_APP_APP_UNINSTALL_H_
+
+#include "base/memory/ref_counted.h"
+
+namespace updater {
+
+class App;
+
+scoped_refptr<App> MakeAppUninstall();
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_APP_APP_UNINSTALL_H_
diff --git a/chrome/updater/app/app_update_all.cc b/chrome/updater/app/app_update_all.cc
new file mode 100644
index 0000000..51c36bf
--- /dev/null
+++ b/chrome/updater/app/app_update_all.cc
@@ -0,0 +1,39 @@
+// 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/updater/app/app_update_all.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "chrome/updater/app/app.h"
+#include "chrome/updater/configurator.h"
+#include "chrome/updater/update_apps.h"
+#include "chrome/updater/update_service.h"
+
+namespace updater {
+
+class AppUpdateAll : public App {
+ private:
+  ~AppUpdateAll() override = default;
+  void FirstTaskRun() override;
+};
+
+// AppUpdateAll triggers an update of all registered applications.
+void AppUpdateAll::FirstTaskRun() {
+  CreateUpdateService()->UpdateAll(base::BindOnce(
+      [](base::OnceCallback<void(int)> quit, update_client::Error error) {
+        VLOG(0) << "UpdateAll complete: error = " << static_cast<int>(error);
+        std::move(quit).Run(static_cast<int>(error));
+      },
+      base::BindOnce(&AppUpdateAll::Shutdown, this)));
+}
+
+scoped_refptr<App> MakeAppUpdateAll() {
+  return base::MakeRefCounted<AppUpdateAll>();
+}
+
+}  // namespace updater
diff --git a/chrome/updater/app/app_update_all.h b/chrome/updater/app/app_update_all.h
new file mode 100644
index 0000000..5af7effc
--- /dev/null
+++ b/chrome/updater/app/app_update_all.h
@@ -0,0 +1,18 @@
+// 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_UPDATER_APP_APP_UPDATE_ALL_H_
+#define CHROME_UPDATER_APP_APP_UPDATE_ALL_H_
+
+#include "base/memory/ref_counted.h"
+
+namespace updater {
+
+class App;
+
+scoped_refptr<App> MakeAppUpdateAll();
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_APP_APP_UPDATE_ALL_H_
diff --git a/chrome/updater/mac/BUILD.gn b/chrome/updater/mac/BUILD.gn
index af6dc66..e9b747f 100644
--- a/chrome/updater/mac/BUILD.gn
+++ b/chrome/updater/mac/BUILD.gn
@@ -38,7 +38,7 @@
     "//base",
     "//chrome/updater:base",
     "//chrome/updater:version_header",
-    "//chrome/updater/server",
+    "//chrome/updater/server/mac:protocol",
     "//components/update_client",
   ]
 
diff --git a/chrome/updater/server/BUILD.gn b/chrome/updater/server/BUILD.gn
deleted file mode 100644
index bce2419a..0000000
--- a/chrome/updater/server/BUILD.gn
+++ /dev/null
@@ -1,12 +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.
-
-group("server") {
-  if (is_mac) {
-    public_deps = [ "//chrome/updater/server/mac" ]
-  }
-  if (is_win) {
-    public_deps = [ "//chrome/updater/server/win" ]
-  }
-}
diff --git a/chrome/updater/server/mac/BUILD.gn b/chrome/updater/server/mac/BUILD.gn
index b85c0535..143d98a 100644
--- a/chrome/updater/server/mac/BUILD.gn
+++ b/chrome/updater/server/mac/BUILD.gn
@@ -2,18 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-source_set("mac") {
-  sources = [
-    "server.h",
-    "server.mm",
-    "service_delegate.h",
-    "service_delegate.mm",
-    "service_protocol.h",
-  ]
+source_set("protocol") {
+  sources = [ "service_protocol.h" ]
 
-  deps = [
-    "//base",
-    "//chrome/updater:base",
-    "//chrome/updater:version_header",
-  ]
+  deps = [ "//chrome/updater:base" ]
 }
diff --git a/chrome/updater/server/mac/server.h b/chrome/updater/server/mac/server.h
index 18bac40d..29a2c2bf 100644
--- a/chrome/updater/server/mac/server.h
+++ b/chrome/updater/server/mac/server.h
@@ -5,13 +5,14 @@
 #ifndef CHROME_UPDATER_SERVER_MAC_SERVER_H_
 #define CHROME_UPDATER_SERVER_MAC_SERVER_H_
 
-#include <memory>
+#include "base/memory/ref_counted.h"
 
 namespace updater {
 
-class UpdateService;
+class App;
 
-int RunServer(std::unique_ptr<UpdateService> update_service);
-}
+scoped_refptr<App> MakeAppServer();
+
+}  // namespace updater
 
 #endif  // CHROME_UPDATER_SERVER_MAC_SERVER_H_
diff --git a/chrome/updater/server/mac/server.mm b/chrome/updater/server/mac/server.mm
index 7a25969..4d51a4a 100644
--- a/chrome/updater/server/mac/server.mm
+++ b/chrome/updater/server/mac/server.mm
@@ -8,29 +8,58 @@
 #include <xpc/xpc.h>
 
 #include "base/mac/scoped_nsobject.h"
+#include "base/memory/ref_counted.h"
 #include "base/strings/sys_string_conversions.h"
-#import "chrome/updater/server/mac/service_delegate.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "chrome/updater/app/app.h"
+#import "chrome/updater/configurator.h"
+#include "chrome/updater/server/mac/service_delegate.h"
 #import "chrome/updater/update_service.h"
+#include "chrome/updater/update_service_in_process.h"
 #include "chrome/updater/updater_version.h"
 
 namespace updater {
 
-int RunServer(std::unique_ptr<UpdateService> update_service) {
+class AppServer : public App {
+ public:
+  AppServer();
+
+ private:
+  ~AppServer() override;
+  void Initialize() override;
+  void FirstTaskRun() override;
+
+  scoped_refptr<Configurator> config_;
+  base::scoped_nsobject<CRUUpdateCheckXPCServiceDelegate> delegate_;
+  base::scoped_nsobject<NSXPCListener> listener_;
+};
+
+AppServer::AppServer() = default;
+AppServer::~AppServer() = default;
+
+void AppServer::Initialize() {
+  config_ = base::MakeRefCounted<Configurator>();
+}
+
+void AppServer::FirstTaskRun() {
   @autoreleasepool {
     std::string service_name = MAC_BUNDLE_IDENTIFIER_STRING;
     service_name.append(".UpdaterXPCService");
-    base::scoped_nsobject<CRUUpdateCheckXPCServiceDelegate> delegate(
-        [[CRUUpdateCheckXPCServiceDelegate alloc]
-            initWithUpdateService:std::move(update_service)]);
+    delegate_.reset([[CRUUpdateCheckXPCServiceDelegate alloc]
+        initWithUpdateService:std::make_unique<UpdateServiceInProcess>(
+                                  config_)]);
 
-    NSXPCListener* listener = [[NSXPCListener alloc]
-        initWithMachServiceName:base::SysUTF8ToNSString(service_name)];
-    listener.delegate = delegate.get();
+    listener_.reset([[NSXPCListener alloc]
+        initWithMachServiceName:base::SysUTF8ToNSString(service_name)]);
+    listener_.get().delegate = delegate_.get();
 
-    [listener resume];
-
-    return 0;
+    [listener_ resume];
   }
 }
 
+scoped_refptr<App> MakeAppServer() {
+  return base::MakeRefCounted<AppServer>();
+}
+
 }  // namespace updater
diff --git a/chrome/updater/server/win/BUILD.gn b/chrome/updater/server/win/BUILD.gn
index c8db811..d96da1e 100644
--- a/chrome/updater/server/win/BUILD.gn
+++ b/chrome/updater/server/win/BUILD.gn
@@ -4,25 +4,6 @@
 
 import("//build/toolchain/win/midl.gni")
 
-source_set("win") {
-  sources = [
-    "server.cc",
-    "server.h",
-    "service_main.cc",
-    "service_main.h",
-  ]
-
-  configs -= [ "//build/config/win:winver" ]
-  configs += [ ":winver" ]
-
-  deps = [
-    ":updater_idl_idl",
-    "//base",
-    "//chrome/updater:base",
-    "//chrome/updater/win:constants",
-  ]
-}
-
 midl("updater_idl_idl") {
   sources = [ "updater_idl.idl" ]
 
diff --git a/chrome/updater/server/win/server.cc b/chrome/updater/server/win/server.cc
index 3234444..fa776db 100644
--- a/chrome/updater/server/win/server.cc
+++ b/chrome/updater/server/win/server.cc
@@ -11,12 +11,62 @@
 
 #include "chrome/updater/server/win/server.h"
 
+#include <windows.h>
+#include <wrl/implements.h>
+#include <wrl/module.h>
+
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/win/scoped_com_initializer.h"
+#include "chrome/updater/app/app.h"
+#include "chrome/updater/configurator.h"
 #include "chrome/updater/update_service.h"
+#include "chrome/updater/update_service_in_process.h"
 
 namespace updater {
 
+// This class is responsible for the lifetime of the COM server, as well as
+// class factory registration.
+class ComServer : public App {
+ public:
+  ComServer();
+
+ private:
+  ~ComServer() override = default;
+
+  void Initialize() override;
+
+  void FirstTaskRun() override;
+
+  // Registers and unregisters the out-of-process COM class factories.
+  HRESULT RegisterClassObject();
+  void UnregisterClassObject();
+
+  // Waits until the last COM object is released.
+  void WaitForExitSignal();
+
+  // Called when the last object is released.
+  void SignalExit();
+
+  // Creates an out-of-process WRL Module.
+  void CreateWRLModule();
+
+  // Handles object unregistration then triggers program shutdown.
+  void Stop();
+
+  // Identifier of registered class objects used for unregistration.
+  DWORD cookies_[1] = {};
+
+  // While this object lives, COM can be used by all threads in the program.
+  base::win::ScopedCOMInitializer com_initializer_;
+
+  // The UpdateService to use for handling COM requests.
+  std::unique_ptr<UpdateService> service_;
+
+  // The updater's Configurator.
+  scoped_refptr<Configurator> config_;
+};
+
 HRESULT UpdaterImpl::CheckForUpdate(const base::char16* app_id) {
   return E_NOTIMPL;
 }
@@ -34,21 +84,7 @@
 }
 
 ComServer::ComServer()
-    : exit_signal_(base::WaitableEvent::ResetPolicy::MANUAL,
-                   base::WaitableEvent::InitialState::NOT_SIGNALED) {}
-
-int ComServer::RunComServer() {
-  // Initialize COM for the current thread.
-  base::win::ScopedCOMInitializer com_initializer(
-      base::win::ScopedCOMInitializer::kMTA);
-  if (!com_initializer.Succeeded()) {
-    PLOG(ERROR) << "Failed to initialize COM";
-    return -1;
-  }
-
-  // Run the COM server.
-  return Run();
-}
+    : com_initializer_(base::win::ScopedCOMInitializer::kMTA) {}
 
 HRESULT ComServer::RegisterClassObject() {
   auto& module = Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::GetModule();
@@ -100,32 +136,35 @@
     LOG(ERROR) << "UnregisterCOMObject failed; hr: " << hr;
 }
 
-void ComServer::WaitForExitSignal() {
-  exit_signal_.Wait();
-}
-
-void ComServer::SignalExit() {
-  exit_signal_.Signal();
-}
-
 void ComServer::CreateWRLModule() {
-  Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::Create(
-      this, &ComServer::SignalExit);
+  Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::Create(this,
+                                                            &ComServer::Stop);
 }
 
-HRESULT ComServer::Run() {
+void ComServer::Stop() {
+  UnregisterClassObject();
+  Shutdown(0);
+}
+
+void ComServer::Initialize() {
+  config_ = base::MakeRefCounted<Configurator>();
+}
+
+void ComServer::FirstTaskRun() {
+  if (!com_initializer_.Succeeded()) {
+    PLOG(ERROR) << "Failed to initialize COM";
+    Shutdown(-1);
+    return;
+  }
+  service_ = std::make_unique<UpdateServiceInProcess>(config_);
   CreateWRLModule();
   HRESULT hr = RegisterClassObject();
-  if (SUCCEEDED(hr)) {
-    WaitForExitSignal();
-    UnregisterClassObject();
-  }
-
-  return hr;
+  if (FAILED(hr))
+    Shutdown(hr);
 }
 
-int RunServer(std::unique_ptr<UpdateService> update_service) {
-  return ComServer().RunComServer();
+scoped_refptr<App> MakeAppServer() {
+  return base::MakeRefCounted<ComServer>();
 }
 
 }  // namespace updater
diff --git a/chrome/updater/server/win/server.h b/chrome/updater/server/win/server.h
index 85d4abb..08835b280 100644
--- a/chrome/updater/server/win/server.h
+++ b/chrome/updater/server/win/server.h
@@ -5,20 +5,15 @@
 #ifndef CHROME_UPDATER_SERVER_WIN_SERVER_H_
 #define CHROME_UPDATER_SERVER_WIN_SERVER_H_
 
-#include <windows.h>
-
 #include <wrl/implements.h>
 #include <wrl/module.h>
-#include <memory>
 
+#include "base/memory/ref_counted.h"
 #include "base/strings/string16.h"
-#include "base/synchronization/waitable_event.h"
 #include "chrome/updater/server/win/updater_idl.h"
 
 namespace updater {
 
-class UpdateService;
-
 // This class implements the IUpdater interface and exposes it as a COM object.
 class UpdaterImpl
     : public Microsoft::WRL::RuntimeClass<
@@ -41,45 +36,9 @@
   ~UpdaterImpl() override = default;
 };
 
-// This class is responsible for the lifetime of the COM server, as well as
-// class factory registration.
-class ComServer {
- public:
-  ComServer();
-  ComServer(const ComServer&) = delete;
-  ComServer& operator=(const ComServer&) = delete;
-  ~ComServer() = default;
+class App;
 
-  // Main entry point for the COM server.
-  int RunComServer();
-
- private:
-  // Registers and unregisters the out-of-process COM class factories.
-  HRESULT RegisterClassObject();
-  void UnregisterClassObject();
-
-  // Waits until the last COM object is released.
-  void WaitForExitSignal();
-
-  // Called when the last object is released.
-  void SignalExit();
-
-  // Creates an out-of-process WRL Module.
-  void CreateWRLModule();
-
-  // Handles object registration, message loop, and unregistration. Returns
-  // when all registered objects are released.
-  HRESULT Run();
-
-  // Identifier of registered class objects used for unregistration.
-  DWORD cookies_[1] = {};
-
-  // This event is signaled when the last COM instance is released.
-  base::WaitableEvent exit_signal_;
-};
-
-// Sets up and runs the server.
-int RunServer(std::unique_ptr<UpdateService> update_service);
+scoped_refptr<App> MakeAppServer();
 
 }  // namespace updater
 
diff --git a/chrome/updater/update_apps.cc b/chrome/updater/update_apps.cc
deleted file mode 100644
index eecb58a..0000000
--- a/chrome/updater/update_apps.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/updater/update_apps.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/run_loop.h"
-#include "base/task/single_thread_task_executor.h"
-#include "chrome/updater/update_service.h"
-
-namespace updater {
-
-int UpdateApps() {
-  base::SingleThreadTaskExecutor main_task_executor;
-  base::RunLoop runloop;
-  std::unique_ptr<UpdateService> service = CreateUpdateService();
-  service->UpdateAll(base::BindOnce(
-      [](base::OnceClosure quit, update_client::Error error) {
-        VLOG(0) << "UpdateAll complete: error = " << static_cast<int>(error);
-        std::move(quit).Run();
-      },
-      runloop.QuitWhenIdleClosure()));
-  runloop.Run();
-  return 0;
-}
-
-}  // namespace updater
diff --git a/chrome/updater/update_apps.h b/chrome/updater/update_apps.h
index 6e1c76d..6a89f54 100644
--- a/chrome/updater/update_apps.h
+++ b/chrome/updater/update_apps.h
@@ -13,9 +13,6 @@
 // A factory method to create an UpdateService class instance.
 std::unique_ptr<UpdateService> CreateUpdateService();
 
-// Updates all registered applications.
-int UpdateApps();
-
 }  // namespace updater
 
 #endif  // CHROME_UPDATER_UPDATE_APPS_H_
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index 738c7d5..51c20de2 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -8,28 +8,23 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
-#include "base/run_loop.h"
-#include "base/task/thread_pool/thread_pool_instance.h"
 #include "build/build_config.h"
+#include "chrome/updater/app/app.h"
+#include "chrome/updater/app/app_uninstall.h"
+#include "chrome/updater/app/app_update_all.h"
 #include "chrome/updater/configurator.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/crash_client.h"
 #include "chrome/updater/crash_reporter.h"
-#include "chrome/updater/update_apps.h"
-#include "chrome/updater/update_service_in_process.h"
-#include "chrome/updater/updater_version.h"
 #include "chrome/updater/util.h"
-#include "components/crash/core/common/crash_key.h"
 
 #if defined(OS_WIN)
 #include "chrome/updater/server/win/server.h"
 #include "chrome/updater/server/win/service_main.h"
 #include "chrome/updater/win/install_app.h"
-#include "chrome/updater/win/setup/uninstall.h"
 #endif
 
 #if defined(OS_MACOSX)
-#include "chrome/updater/mac/setup/setup.h"
 #include "chrome/updater/server/mac/server.h"
 #endif
 
@@ -47,14 +42,6 @@
 
 namespace {
 
-void ThreadPoolStart() {
-  base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Updater");
-}
-
-void ThreadPoolStop() {
-  base::ThreadPoolInstance::Get()->Shutdown();
-}
-
 // The log file is created in DIR_LOCAL_APP_DATA or DIR_APP_DATA.
 void InitLogging(const base::CommandLine& command_line) {
   logging::LoggingSettings settings;
@@ -71,80 +58,33 @@
   VLOG(1) << "Log file " << settings.log_file_path;
 }
 
-void InitializeUpdaterMain() {
-  crash_reporter::InitializeCrashKeys();
-
-  static crash_reporter::CrashKeyString<16> crash_key_process_type(
-      "process_type");
-  crash_key_process_type.Set("updater");
-
-  if (CrashClient::GetInstance()->InitializeCrashReporting())
-    VLOG(1) << "Crash reporting initialized.";
-  else
-    VLOG(1) << "Crash reporting is not available.";
-
-  StartCrashReporter(UPDATER_VERSION_STRING);
-
-  ThreadPoolStart();
-}
-
-void TerminateUpdaterMain() {
-  ThreadPoolStop();
-}
-
-void UpdaterUpdateApps() {
-  UpdateApps();
-}
-
-int UpdaterInstallApp() {
-#if defined(OS_WIN)
-  // TODO(sorin): pick up the app id from the tag. https://crbug.com/1014298
-  return InstallApp({kChromeAppId});
-#else
-  NOTREACHED();
-  return -1;
-#endif
-}
-
-int UpdaterUninstall() {
-#if defined(OS_WIN) || defined(OS_MACOSX)
-  return Uninstall(false);
-#else
-  return -1;
-#endif
-}
-
 }  // namespace
 
 int HandleUpdaterCommands(const base::CommandLine* command_line) {
   DCHECK(!command_line->HasSwitch(kCrashHandlerSwitch));
 
-#if defined(OS_WIN) || defined(OS_MACOSX)
-  if (command_line->HasSwitch(kServerSwitch)) {
-    return RunServer(std::make_unique<UpdateServiceInProcess>(
-        base::MakeRefCounted<Configurator>()));
-  }
-#endif
-
-#if defined(OS_WIN)
-  if (command_line->HasSwitch(kComServiceSwitch))
-    return ServiceMain::RunComService(command_line);
-#endif
-
   if (command_line->HasSwitch(kCrashMeSwitch)) {
     int* ptr = nullptr;
     return *ptr;
   }
 
+  if (command_line->HasSwitch(kServerSwitch)) {
+    return MakeAppServer()->Run();
+  }
+
+#if defined(OS_WIN)
+  if (command_line->HasSwitch(kComServiceSwitch))
+    return ServiceMain::RunComService(command_line);
+
   if (command_line->HasSwitch(kInstallSwitch))
-    return UpdaterInstallApp();
+    return InstallApp({kChromeAppId});
+#endif
 
   if (command_line->HasSwitch(kUninstallSwitch))
-    return UpdaterUninstall();
+    return MakeAppUninstall()->Run();
 
   if (command_line->HasSwitch(kUpdateAppsSwitch)) {
-    UpdaterUpdateApps();
-    return 0;
+    return MakeAppUpdateAll()->Run();
   }
 
   VLOG(1) << "Unknown command line switch.";
@@ -165,10 +105,7 @@
   if (command_line->HasSwitch(kCrashHandlerSwitch))
     return CrashReporterMain();
 
-  InitializeUpdaterMain();
-  const auto result = HandleUpdaterCommands(command_line);
-  TerminateUpdaterMain();
-  return result;
+  return HandleUpdaterCommands(command_line);
 }
 
 }  // namespace updater
diff --git a/chrome/updater/win/BUILD.gn b/chrome/updater/win/BUILD.gn
index 158526e0..6fa984c 100644
--- a/chrome/updater/win/BUILD.gn
+++ b/chrome/updater/win/BUILD.gn
@@ -189,7 +189,6 @@
     "//base",
     "//base/test:test_support",
     "//chrome/updater:version_header",
-    "//chrome/updater/server/win",
     "//chrome/updater/win/test:test_executables",
     "//chrome/updater/win/test:test_strings",
     "//testing/gtest",
diff --git a/chrome/updater/win/test/BUILD.gn b/chrome/updater/win/test/BUILD.gn
index f3b9809..1340a2907 100644
--- a/chrome/updater/win/test/BUILD.gn
+++ b/chrome/updater/win/test/BUILD.gn
@@ -60,6 +60,6 @@
     "//base",
     "//base/test:test_support",
     "//build/win:default_exe_manifest",
-    "//chrome/updater/server/win",
+    "//chrome/updater:lib",
   ]
 }
diff --git a/chromecast/crash/app_state_tracker.cc b/chromecast/crash/app_state_tracker.cc
index e11956cf..f04b96d 100644
--- a/chromecast/crash/app_state_tracker.cc
+++ b/chromecast/crash/app_state_tracker.cc
@@ -14,6 +14,7 @@
   std::string previous_app;
   std::string current_app;
   std::string last_launched_app;
+  std::string stadia_session_id;
 };
 
 CurrentAppState* GetAppState() {
@@ -41,6 +42,11 @@
 }
 
 // static
+std::string AppStateTracker::GetStadiaSessionId() {
+  return GetAppState()->stadia_session_id;
+}
+
+// static
 void AppStateTracker::SetLastLaunchedApp(const std::string& app_id) {
   GetAppState()->last_launched_app = app_id;
 
@@ -59,4 +65,15 @@
   crash_keys::previous_app.Set(app_state->previous_app);
 }
 
+// static
+void AppStateTracker::SetStadiaSessionId(const std::string& stadia_session_id) {
+  if (!stadia_session_id.empty()) {
+    GetAppState()->stadia_session_id = stadia_session_id;
+    crash_keys::stadia_session_id.Set(stadia_session_id);
+  } else {
+    GetAppState()->stadia_session_id.clear();
+    crash_keys::stadia_session_id.Clear();
+  }
+}
+
 }  // namespace chromecast
diff --git a/chromecast/crash/app_state_tracker.h b/chromecast/crash/app_state_tracker.h
index 3df8686c..513d42c 100644
--- a/chromecast/crash/app_state_tracker.h
+++ b/chromecast/crash/app_state_tracker.h
@@ -17,6 +17,10 @@
   // The current app becomes the previous app, |app_id| becomes the current app.
   static void SetCurrentApp(const std::string& app_id);
 
+  // Set the Stadia session ID, when a Stadia session starts running.
+  // Clear the Stadia session ID by passing in an empty string
+  static void SetStadiaSessionId(const std::string& stadia_session_id);
+
   // Returns the id of the app that was last attempted to launch.
   static std::string GetLastLaunchedApp();
 
@@ -25,6 +29,9 @@
 
   // Returns the id of the app which was previously active.
   static std::string GetPreviousApp();
+
+  // Returns the Stadia session ID, if a Stadia session is running.
+  static std::string GetStadiaSessionId();
 };
 
 }  // namespace chromecast
diff --git a/chromecast/crash/cast_crash_keys.cc b/chromecast/crash/cast_crash_keys.cc
index f2133d1..0237a5c 100644
--- a/chromecast/crash/cast_crash_keys.cc
+++ b/chromecast/crash/cast_crash_keys.cc
@@ -11,5 +11,7 @@
 
 crash_reporter::CrashKeyString<64> previous_app("previous_app");
 
+crash_reporter::CrashKeyString<64> stadia_session_id("stadia_session_id");
+
 }  // namespace crash_keys
 }  // namespace chromecast
diff --git a/chromecast/crash/cast_crash_keys.h b/chromecast/crash/cast_crash_keys.h
index 83dfaf46..6f61531 100644
--- a/chromecast/crash/cast_crash_keys.h
+++ b/chromecast/crash/cast_crash_keys.h
@@ -14,6 +14,8 @@
 
 extern crash_reporter::CrashKeyString<64> previous_app;
 
+extern crash_reporter::CrashKeyString<64> stadia_session_id;
+
 }  // namespace crash_keys
 }  // namespace chromecast
 
diff --git a/chromecast/crash/linux/crash_util.cc b/chromecast/crash/linux/crash_util.cc
index a2de8f6..66ed4901 100644
--- a/chromecast/crash/linux/crash_util.cc
+++ b/chromecast/crash/linux/crash_util.cc
@@ -50,7 +50,8 @@
       "",  // suffix
       AppStateTracker::GetPreviousApp(), AppStateTracker::GetCurrentApp(),
       AppStateTracker::GetLastLaunchedApp(), CAST_BUILD_RELEASE,
-      CAST_BUILD_INCREMENTAL, "" /* reason */);
+      CAST_BUILD_INCREMENTAL, "", /* reason */
+      AppStateTracker::GetStadiaSessionId());
   DummyMinidumpGenerator minidump_generator(existing_minidump_path);
 
   base::FilePath filename = base::FilePath(existing_minidump_path).BaseName();
diff --git a/chromecast/crash/linux/dump_info.cc b/chromecast/crash/linux/dump_info.cc
index f3516af..926c274 100644
--- a/chromecast/crash/linux/dump_info.cc
+++ b/chromecast/crash/linux/dump_info.cc
@@ -32,6 +32,7 @@
 const char kReleaseVersionKey[] = "release_version";
 const char kBuildNumberKey[] = "build_number";
 const char kReasonKey[] = "reason";
+const char kStadiaSessionIdKey[] = "stadia_session_id";
 
 }  // namespace
 
@@ -75,6 +76,7 @@
   entry->SetString(kReleaseVersionKey, params_.cast_release_version);
   entry->SetString(kBuildNumberKey, params_.cast_build_number);
   entry->SetString(kReasonKey, params_.reason);
+  entry->SetString(kStadiaSessionIdKey, params_.stadia_session_id);
 
   return result;
 }
@@ -129,6 +131,8 @@
     ++num_params;
   if (dict->GetString(kReasonKey, &params_.reason))
     ++num_params;
+  if (dict->GetString(kStadiaSessionIdKey, &params_.stadia_session_id))
+    ++num_params;
 
   // Disallow extraneous params
   if (dict->size() != num_params)
diff --git a/chromecast/crash/linux/minidump_params.cc b/chromecast/crash/linux/minidump_params.cc
index ff81e2b..7f69427 100644
--- a/chromecast/crash/linux/minidump_params.cc
+++ b/chromecast/crash/linux/minidump_params.cc
@@ -13,7 +13,8 @@
                                const std::string& p_last_app_name,
                                const std::string& p_cast_release_version,
                                const std::string& p_cast_build_number,
-                               const std::string& p_reason)
+                               const std::string& p_reason,
+                               const std::string& p_stadia_session_id)
     : process_uptime(p_process_uptime),
       suffix(p_suffix),
       previous_app_name(p_previous_app_name),
@@ -21,7 +22,8 @@
       last_app_name(p_last_app_name),
       cast_release_version(p_cast_release_version),
       cast_build_number(p_cast_build_number),
-      reason(p_reason) {}
+      reason(p_reason),
+      stadia_session_id(p_stadia_session_id) {}
 
 MinidumpParams::MinidumpParams() : process_uptime(0) {}
 
diff --git a/chromecast/crash/linux/minidump_params.h b/chromecast/crash/linux/minidump_params.h
index 9cf92d8..fb07cb4 100644
--- a/chromecast/crash/linux/minidump_params.h
+++ b/chromecast/crash/linux/minidump_params.h
@@ -20,7 +20,8 @@
                  const std::string& p_last_app_name,
                  const std::string& p_cast_release_version,
                  const std::string& p_cast_build_number,
-                 const std::string& p_reason);
+                 const std::string& p_reason,
+                 const std::string& p_stadia_session_id);
   MinidumpParams(const MinidumpParams& params);
   ~MinidumpParams();
 
@@ -35,6 +36,8 @@
   std::string cast_build_number;
   // Reason for crash, if one is available.
   std::string reason;
+  // Stadia Session ID, if a Stadia session was running at the time of crash.
+  std::string stadia_session_id;
 };
 
 }  // namespace chromecast
diff --git a/chromecast/crash/linux/minidump_uploader.cc b/chromecast/crash/linux/minidump_uploader.cc
index d63a128..4d0c114 100644
--- a/chromecast/crash/linux/minidump_uploader.cc
+++ b/chromecast/crash/linux/minidump_uploader.cc
@@ -254,6 +254,9 @@
     if (!dump.params().reason.empty()) {
       g.SetParameter("reason", dump.params().reason);
     }
+    if (!dump.params().stadia_session_id.empty()) {
+      g.SetParameter("stadia_session_id", dump.params().stadia_session_id);
+    }
 
     std::string response;
     if (!g.Upload(&response)) {
diff --git a/chromecast/crash/linux/minidump_uploader_unittest.cc b/chromecast/crash/linux/minidump_uploader_unittest.cc
index 9e67ce50..74e9fd0 100644
--- a/chromecast/crash/linux/minidump_uploader_unittest.cc
+++ b/chromecast/crash/linux/minidump_uploader_unittest.cc
@@ -97,7 +97,7 @@
     // Must pass in non-empty MinidumpParams to circumvent the internal checks.
     std::unique_ptr<DumpInfo> dump(new DumpInfo(
         minidump_path.value(), logfile_path.value(), base::Time::Now(),
-        MinidumpParams(0, "_", "_", "_", "_", "_", "_", "_")));
+        MinidumpParams(0, "_", "_", "_", "_", "_", "_", "_", "_")));
 
     CHECK(AppendLockFile(lockfile_.value(), metadata_.value(), *dump));
     base::File minidump(
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 6d40519f..e267e94 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -91,7 +91,11 @@
   }
 
   if (is_printing_ppd_provider_v3) {
-    sources += [ "printing/ppd_provider_v3.cc" ]
+    sources += [
+      "printing/ppd_provider_v3.cc",
+      "printing/printer_config_cache.cc",
+      "printing/printer_config_cache.h",
+    ]
   } else {
     sources += [ "printing/ppd_provider.cc" ]
   }
@@ -194,7 +198,6 @@
     "printing/epson_driver_matching_unittest.cc",
     "printing/ppd_cache_unittest.cc",
     "printing/ppd_line_reader_unittest.cc",
-    "printing/ppd_provider_unittest.cc",
     "printing/printer_configuration_unittest.cc",
     "printing/printer_translator_unittest.cc",
     "printing/usb_printer_id_unittest.cc",
@@ -203,6 +206,12 @@
     "test/run_all_unittests.cc",
   ]
 
+  if (is_printing_ppd_provider_v3) {
+    sources += [ "printing/printer_config_cache_unittest.cc" ]
+  } else {
+    sources += [ "printing/ppd_provider_unittest.cc" ]
+  }
+
   data = [ "test/data/" ]
 }
 
diff --git a/chromeos/printing/ppd_provider_v3.cc b/chromeos/printing/ppd_provider_v3.cc
index 2bddfa5..e8636f10 100644
--- a/chromeos/printing/ppd_provider_v3.cc
+++ b/chromeos/printing/ppd_provider_v3.cc
@@ -91,8 +91,8 @@
     const PpdProvider::Options& options) {
   // TODO(crbug.com/888189): use |loader_factory| and do away with
   // |ppd_cache|.
-  return base::MakeRefCounted<PpdProvider>(browser_locale, current_version,
-                                           options);
+  return base::MakeRefCounted<PpdProviderImpl>(browser_locale, current_version,
+                                               options);
 }
 
 }  // namespace chromeos
diff --git a/chromeos/printing/printer_config_cache.cc b/chromeos/printing/printer_config_cache.cc
new file mode 100644
index 0000000..06bb6a1
--- /dev/null
+++ b/chromeos/printing/printer_config_cache.cc
@@ -0,0 +1,217 @@
+// 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 "chromeos/printing/printer_config_cache.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
+#include "base/task/post_task.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "url/gurl.h"
+
+namespace chromeos {
+
+namespace {
+
+// Defines the serving root in which all PPDs and PPD metadata reside.
+const char kServingRoot[] =
+    "https://printerconfigurations.googleusercontent.com/"
+    "chromeos_printing/";
+
+// Prepends the serving root to |name|, returning the result.
+std::string PrependServingRoot(const std::string& name) {
+  return base::StrCat({base::StringPiece(kServingRoot), name});
+}
+
+// Accepts a relative |path| to a value in the Chrome OS Printing
+// serving root) and returns a resource request to satisfy the same.
+std::unique_ptr<network::ResourceRequest> FormRequest(const std::string& path) {
+  GURL full_url(PrependServingRoot(path));
+  if (!full_url.is_valid()) {
+    return nullptr;
+  }
+
+  auto request = std::make_unique<network::ResourceRequest>();
+  request->url = full_url;
+
+  request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
+  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  return request;
+}
+
+}  // namespace
+
+// In case of fetch failure, only the key is meaningful feedback.
+// static
+PrinterConfigCache::FetchResult PrinterConfigCache::FetchResult::Failure(
+    const std::string& key) {
+  return PrinterConfigCache::FetchResult{false, key, std::string(),
+                                         base::Time()};
+}
+
+// static
+PrinterConfigCache::FetchResult PrinterConfigCache::FetchResult::Success(
+    const std::string& key,
+    const std::string& contents,
+    base::Time time_of_fetch) {
+  return PrinterConfigCache::FetchResult{true, key, contents, time_of_fetch};
+}
+
+class PrinterConfigCacheImpl : public PrinterConfigCache {
+ public:
+  explicit PrinterConfigCacheImpl(
+      const base::Clock* clock,
+      network::mojom::URLLoaderFactory* loader_factory)
+      : clock_(clock), loader_factory_(loader_factory), weak_factory_(this) {}
+
+  ~PrinterConfigCacheImpl() override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  }
+
+  void Fetch(const std::string& key,
+             base::TimeDelta expiration,
+             FetchCallback cb) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    // Try to answer this fetch request locally.
+    const auto& finding = cache_.find(key);
+    if (finding != cache_.end()) {
+      const Entry& entry = finding->second;
+      if (entry.time_of_fetch + expiration > clock_->Now()) {
+        std::move(cb).Run(
+            FetchResult::Success(key, entry.contents, entry.time_of_fetch));
+        return;
+      }
+    }
+
+    // We couldn't answer this request locally. Issue a networked fetch
+    // and defer the answer to when we hear back.
+    auto context = std::make_unique<FetchContext>(key, std::move(cb));
+    fetch_queue_.push(std::move(context));
+    TryToStartNetworkedFetch();
+  }
+
+  void Drop(const std::string& key) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    cache_.erase(key);
+  }
+
+ private:
+  // A FetchContext saves off the key and the FetchCallback that a
+  // caller passes to PrinterConfigCacheImpl::Fetch().
+  struct FetchContext {
+    const std::string key;
+    PrinterConfigCache::FetchCallback cb;
+
+    FetchContext(const std::string& arg_key,
+                 PrinterConfigCache::FetchCallback arg_cb)
+        : key(arg_key), cb(std::move(arg_cb)) {}
+    ~FetchContext() = default;
+  };
+
+  // If a PrinterConfigCache maps keys to values, then Entry structs
+  // represent values.
+  struct Entry {
+    std::string contents;
+    base::Time time_of_fetch;
+
+    Entry(const std::string& arg_contents, base::Time time)
+        : contents(arg_contents), time_of_fetch(time) {}
+    ~Entry() = default;
+  };
+
+  void TryToStartNetworkedFetch() {
+    // Either
+    // 1. a networked fetch is already in flight or
+    // 2. there are no more pending networked fetches to act upon.
+    // In either case, we can't do anything at the moment; back off
+    // and let a future call to Fetch() or FinishNetworkedFetch()
+    // return here to try again.
+    if (fetcher_ || fetch_queue_.empty()) {
+      return;
+    }
+
+    std::unique_ptr<FetchContext> context = std::move(fetch_queue_.front());
+    fetch_queue_.pop();
+    auto request = FormRequest(context->key);
+
+    // TODO(crbug.com/888189): add traffic annotation.
+    fetcher_ = network::SimpleURLLoader::Create(std::move(request),
+                                                MISSING_TRAFFIC_ANNOTATION);
+    fetcher_->DownloadToString(
+        loader_factory_,
+        base::BindOnce(&PrinterConfigCacheImpl::FinishNetworkedFetch,
+                       weak_factory_.GetWeakPtr(), std::move(context)),
+        network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
+  }
+
+  // Called by |fetcher_| once DownloadToString() completes.
+  void FinishNetworkedFetch(std::unique_ptr<FetchContext> context,
+                            std::unique_ptr<std::string> contents) {
+    // Wherever |fetcher_| works its sorcery, it had better have posted
+    // back onto _our_ sequence.
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    if (fetcher_->NetError() == net::Error::OK) {
+      // We only want to update our local cache if the |fetcher_|
+      // succeeded; otherwise, prefer to either retain the stale entry
+      // (if extant) or retain no entry at all (if not).
+      const Entry newly_inserted = Entry(*contents, clock_->Now());
+      cache_.insert_or_assign(context->key, newly_inserted);
+      std::move(context->cb)
+          .Run(FetchResult::Success(context->key, newly_inserted.contents,
+                                    newly_inserted.time_of_fetch));
+    } else {
+      std::move(context->cb).Run(FetchResult::Failure(context->key));
+    }
+
+    fetcher_.reset();
+    TryToStartNetworkedFetch();
+  }
+
+  // The heart of an PrinterConfigCache: the local cache itself.
+  base::flat_map<std::string, Entry> cache_;
+
+  // Enqueues networked requests.
+  base::queue<std::unique_ptr<FetchContext>> fetch_queue_;
+
+  // Dispenses Time objects to mark time of fetch on Entry instances.
+  const base::Clock* clock_;
+
+  // Mutably borrowed from caller at construct-time.
+  network::mojom::URLLoaderFactory* loader_factory_;
+
+  // Talks to the networked service to fetch resources.
+  //
+  // Because this class is sequenced, a non-nullptr value here (observed
+  // on-sequence) denotes an ongoing fetch. See the
+  // TryToStartNetworkedFetch() and FinishNetworkedFetch() methods.
+  std::unique_ptr<network::SimpleURLLoader> fetcher_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  // Dispenses weak pointers to our |fetcher_|. This is necessary
+  // because |this| could be deleted while the loader is in flight
+  // off-sequence.
+  base::WeakPtrFactory<PrinterConfigCacheImpl> weak_factory_;
+};
+
+// static
+std::unique_ptr<PrinterConfigCache> PrinterConfigCache::Create(
+    const base::Clock* clock,
+    network::mojom::URLLoaderFactory* loader_factory) {
+  return std::make_unique<PrinterConfigCacheImpl>(clock, loader_factory);
+}
+
+}  // namespace chromeos
diff --git a/chromeos/printing/printer_config_cache.h b/chromeos/printing/printer_config_cache.h
new file mode 100644
index 0000000..6623759
--- /dev/null
+++ b/chromeos/printing/printer_config_cache.h
@@ -0,0 +1,95 @@
+// 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.
+//
+// The PrinterConfigCache class accepts requests to fetch things from
+// the Chrome OS Printing serving root. It only stores things in memory.
+//
+// In practice, the present class fetches either PPDs or PPD metadata.
+
+#ifndef CHROMEOS_PRINTING_PRINTER_CONFIG_CACHE_H_
+#define CHROMEOS_PRINTING_PRINTER_CONFIG_CACHE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/queue.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_piece.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "chromeos/chromeos_export.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace chromeos {
+
+// A PrinterConfigCache maps keys to values. By convention, keys are
+// relative paths to files in the Chrome OS Printing serving root
+// (hardcoded into this class). In practice, that means keys will either
+// *  start with "metadata_v3/" or
+// *  start with "ppds_for_metadata_v3/."
+//
+// This class must always be constructed on, used on, and destroyed from
+// a sequenced context.
+//
+// TODO(crbug.com/888189): remove CHROMEOS_EXPORT and refactor all this
+// into a dedicated gn component.
+class CHROMEOS_EXPORT PrinterConfigCache {
+ public:
+  static std::unique_ptr<PrinterConfigCache> Create(
+      const base::Clock* clock,
+      network::mojom::URLLoaderFactory* loader_factory);
+  virtual ~PrinterConfigCache() = default;
+
+  // Result of calling Fetch(). The |key| identifies how Fetch() was
+  // originally invoked. The |contents| and |time_of_fetch| are well-
+  // defined iff |succeeded| is true.
+  struct FetchResult {
+    static FetchResult Failure(const std::string& key);
+
+    static FetchResult Success(const std::string& key,
+                               const std::string& contents,
+                               base::Time time_of_fetch);
+
+    bool succeeded;
+    std::string key;
+    std::string contents;
+    base::Time time_of_fetch;
+  };
+
+  // Caller is responsible for providing sequencing of this type.
+  using FetchCallback = base::OnceCallback<void(const FetchResult&)>;
+
+  // Queries the Chrome OS Printing serving root for |key|. Calls |cb|
+  // with the contents. If an entry newer than |expiration| is resident,
+  // calls |cb| immediately with those contents. Caller should not pass
+  // keys with leading slashes.
+  //
+  // Using TimeDelta implies the caller is asking for "some entry not
+  // older than |expiration|," e.g. "metadata_v3/index-00.json that
+  // was fetched within the last 30 minutes."
+  //
+  // Naturally,
+  // *  passing the Max() TimeDelta means "perform this Fetch() with no
+  //    limit on staleness" and
+  // *  passing a zero TimeDelta should practically force a networked
+  //    fetch (less esoteric timing quirks etc.).
+  virtual void Fetch(const std::string& key,
+                     base::TimeDelta expiration,
+                     FetchCallback cb) = 0;
+
+  // Drops Entry corresponding to |key|.
+  virtual void Drop(const std::string& key) = 0;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_PRINTING_PRINTER_CONFIG_CACHE_H_
diff --git a/chromeos/printing/printer_config_cache_unittest.cc b/chromeos/printing/printer_config_cache_unittest.cc
new file mode 100644
index 0000000..eabb44c
--- /dev/null
+++ b/chromeos/printing/printer_config_cache_unittest.cc
@@ -0,0 +1,420 @@
+// 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 "chromeos/printing/printer_config_cache.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/location.h"
+#include "base/run_loop.h"
+#include "base/test/simple_test_clock.h"
+#include "base/test/task_environment.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "net/http/http_status_code.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Maintainer's notes:
+//
+// 1. The use of base::Unretained throughout this suite is appropriate
+//    because the sequences of each test live as long as the test does.
+//    Real consumers probably can't do this.
+// 2. The passage of time is controlled by a mock clock, so most Fetch()
+//    invocations not preceded by clock advancement never hit the
+//    "networked fetch" codepath. In such tests, the values of the
+//    TimeDelta argument are arbitrary and meaningless.
+
+namespace chromeos {
+namespace {
+
+// Defines some resources (URLs and contents) used throughout this
+// test suite.
+
+// Name of the "known-good" resource.
+const char kKnownGoodResourceURL[] =
+    "https://printerconfigurations.googleusercontent.com/chromeos_printing/"
+    "known-good";
+
+// Arbitrary content for the "known-good" resource.
+const char kKnownGoodResourceContent[] = "yakisaba";
+
+// Name of the "known-bad" resource.
+const char kKnownBadResourceURL[] =
+    "https://printerconfigurations.googleusercontent.com/chromeos_printing/"
+    "known-bad";
+
+// Defines an arbitrary time increment by which we advance the Clock.
+constexpr base::TimeDelta kTestingIncrement = base::TimeDelta::FromSeconds(1LL);
+
+// Defines a time of fetch used to construct FetchResult instances that
+// you'll use with the TimeInsensitiveFetchResultEquals matcher.
+constexpr base::Time kUnusedTimeOfFetch;
+
+MATCHER_P(TimeInsensitiveFetchResultEquals, expected, "") {
+  return arg.succeeded == expected.succeeded && arg.key == expected.key &&
+         arg.contents == expected.contents;
+}
+
+MATCHER_P(FetchResultEquals, expected, "") {
+  return arg.succeeded == expected.succeeded && arg.key == expected.key &&
+         arg.contents == expected.contents &&
+         arg.time_of_fetch == expected.time_of_fetch;
+}
+
+class PrinterConfigCacheTest : public ::testing::Test {
+ public:
+  PrinterConfigCacheTest()
+      : task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
+        cache_(PrinterConfigCache::Create(&clock_, &loader_factory_)) {}
+
+  // Sets up the default responses to dispense.
+  void SetUp() override {
+    // Dispenses the "known-good" resource with its content.
+    loader_factory_.AddResponse(kKnownGoodResourceURL,
+                                kKnownGoodResourceContent);
+
+    // Dispenses the "known-bad" resource with no content and an
+    // arbitrary HTTP error.
+    loader_factory_.AddResponse(kKnownBadResourceURL, "",
+                                net::HTTP_NOT_ACCEPTABLE);
+  }
+
+  // Method passed as a FetchCallback (partially bound) to
+  // cache_.Fetch(). Saves the |result| in the |fetched_results_|.
+  // Invokes the |quit_closure| to signal the enclosing RunLoop that
+  // this method has been called.
+  void CaptureFetchResult(base::RepeatingClosure quit_closure,
+                          const PrinterConfigCache::FetchResult& result) {
+    fetched_results_.push_back(result);
+
+    // The caller may elect to pass a default-constructed
+    // RepeatingClosure, indicating that they don't want anything run.
+    if (quit_closure) {
+      quit_closure.Run();
+    }
+  }
+
+  void AdvanceClock(base::TimeDelta amount = kTestingIncrement) {
+    clock_.Advance(amount);
+  }
+
+ protected:
+  // Landing area used to collect Fetch()ed results.
+  std::vector<PrinterConfigCache::FetchResult> fetched_results_;
+
+  // Loader factory for testing loaned to |cache_|.
+  network::TestURLLoaderFactory loader_factory_;
+
+  // Environment for task schedulers.
+  base::test::TaskEnvironment task_environment_;
+
+  // Controlled clock that dispenses times of Fetch().
+  base::SimpleTestClock clock_;
+
+  // Class under test.
+  std::unique_ptr<PrinterConfigCache> cache_;
+};
+
+// Tests that we can succeed in Fetch()ing anything at all.
+TEST_F(PrinterConfigCacheTest, SucceedAtSingleFetch) {
+  base::RunLoop run_loop;
+
+  // Fetches the "known-good" resource.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+          "known-good", base::TimeDelta::FromSeconds(0LL),
+          base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                         base::Unretained(this), run_loop.QuitClosure())));
+  run_loop.Run();
+
+  ASSERT_EQ(fetched_results_.size(), 1ULL);
+  EXPECT_THAT(
+      fetched_results_.front(),
+      TimeInsensitiveFetchResultEquals(PrinterConfigCache::FetchResult::Success(
+          "known-good", "yakisaba", kUnusedTimeOfFetch)));
+}
+
+// Tests that we fail to Fetch() the "known-bad" resource.
+TEST_F(PrinterConfigCacheTest, FailAtSingleFetch) {
+  base::RunLoop run_loop;
+
+  // Fetches the "known-bad" resource.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+          "known-bad", base::TimeDelta::FromSeconds(0LL),
+          base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                         base::Unretained(this), run_loop.QuitClosure())));
+  run_loop.Run();
+
+  ASSERT_EQ(fetched_results_.size(), 1ULL);
+  EXPECT_THAT(fetched_results_.front(),
+              TimeInsensitiveFetchResultEquals(
+                  PrinterConfigCache::FetchResult::Failure("known-bad")));
+}
+
+// Tests that we can force a networked Fetch() by demanding
+// fresh content.
+TEST_F(PrinterConfigCacheTest, RefreshSubsequentFetch) {
+  // Fetches the "known-good" resource with its stock contents.
+  base::RunLoop first_run_loop;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+                     "known-good", base::TimeDelta::FromSeconds(0LL),
+                     base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                                    base::Unretained(this),
+                                    first_run_loop.QuitClosure())));
+  first_run_loop.Run();
+
+  ASSERT_EQ(fetched_results_.size(), 1ULL);
+
+  // To detect a networked fetch, we'll change the served content
+  // and check that the subsequent Fetch() recovers the new content.
+  loader_factory_.AddResponse(kKnownGoodResourceURL, "one Argentinian peso");
+
+  // We've mutated the content; now, this fetches the "known-good"
+  // resource with its new contents.
+  base::RunLoop second_run_loop;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+                     "known-good", base::TimeDelta::FromSeconds(0LL),
+                     base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                                    base::Unretained(this),
+                                    second_run_loop.QuitClosure())));
+  second_run_loop.Run();
+
+  ASSERT_EQ(fetched_results_.size(), 2ULL);
+
+  EXPECT_THAT(
+      fetched_results_,
+      testing::ElementsAre(
+          TimeInsensitiveFetchResultEquals(
+              PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
+                                                       kUnusedTimeOfFetch)),
+          TimeInsensitiveFetchResultEquals(
+              PrinterConfigCache::FetchResult::Success(
+                  "known-good", "one Argentinian peso", kUnusedTimeOfFetch))));
+}
+
+// Tests that we can Fetch() locally cached contents by specifying a
+// wide age limit.
+TEST_F(PrinterConfigCacheTest, LocallyPerformSubsequentFetch) {
+  // Fetches the "known-good" resource with its stock contents.
+  base::RunLoop first_run_loop;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+                     "known-good", base::TimeDelta::FromSeconds(0LL),
+                     base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                                    base::Unretained(this),
+                                    first_run_loop.QuitClosure())));
+  first_run_loop.Run();
+
+  ASSERT_EQ(fetched_results_.size(), 1ULL);
+
+  // As in the RefreshSubsequentFetch test, we'll change the served
+  // content to detect networked fetch requests made.
+  loader_factory_.AddResponse(kKnownGoodResourceURL, "apologize darn you");
+
+  // The "live" content in the serving root has changed; now, we perform
+  // some local fetches without hitting the network. These Fetch()es
+  // will return the stock content.
+  base::RunLoop second_run_loop;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+                     "known-good",
+                     // Avoids hitting the network by using a long
+                     // timeout. Bear in mind that this test controls
+                     // the passage of time, so nonzero timeout is
+                     // "long" here...
+                     base::TimeDelta::FromSeconds(1LL),
+                     base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                                    base::Unretained(this),
+                                    // Avoids quitting this RunLoop.
+                                    base::RepeatingClosure())));
+
+  // Performs a local Fetch() a few more times for no particular reason.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+          "known-good", base::TimeDelta::FromSeconds(3600LL),
+          base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                         base::Unretained(this), base::RepeatingClosure())));
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+          "known-good", base::TimeDelta::FromSeconds(86400LL),
+          base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                         base::Unretained(this), base::RepeatingClosure())));
+
+  // Performs a live Fetch(), returning the live (mutated) contents.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+                     "known-good",
+                     // Forces the networked fetch.
+                     base::TimeDelta::FromSeconds(0LL),
+                     base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                                    base::Unretained(this),
+                                    // Ends our RunLoop.
+                                    second_run_loop.QuitClosure())));
+  second_run_loop.Run();
+
+  ASSERT_EQ(fetched_results_.size(), 5ULL);
+  EXPECT_THAT(
+      fetched_results_,
+      testing::ElementsAre(
+          TimeInsensitiveFetchResultEquals(
+              PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
+                                                       kUnusedTimeOfFetch)),
+          TimeInsensitiveFetchResultEquals(
+              PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
+                                                       kUnusedTimeOfFetch)),
+          TimeInsensitiveFetchResultEquals(
+              PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
+                                                       kUnusedTimeOfFetch)),
+          TimeInsensitiveFetchResultEquals(
+              PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
+                                                       kUnusedTimeOfFetch)),
+          TimeInsensitiveFetchResultEquals(
+              PrinterConfigCache::FetchResult::Success(
+                  "known-good", "apologize darn you", kUnusedTimeOfFetch))));
+}
+
+// Tests that Fetch() respects its |expiration| argument. This is a
+// purely time-bound variation on the LocallyPerformSubsequentFetch
+// test; the served content doesn't change between RunLoops.
+TEST_F(PrinterConfigCacheTest, FetchExpirationIsRespected) {
+  // This Fetch() is given a useful |expiration|, but it won't matter
+  // here since there are no locally resident cache entries at this
+  // time; it'll have to be a networked fetch.
+  base::RunLoop first_run_loop;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+                     "known-good", base::TimeDelta::FromSeconds(32LL),
+                     base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                                    base::Unretained(this),
+                                    first_run_loop.QuitClosure())));
+  first_run_loop.Run();
+  ASSERT_EQ(fetched_results_.size(), 1ULL);
+  const base::Time time_zero = clock_.Now();
+
+  // Advance clock to T+31.
+  AdvanceClock(base::TimeDelta::FromSeconds(31LL));
+
+  // This Fetch() is given the same useful |expiration|; it only matters
+  // in that the clock does not yet indicate that the locally resident
+  // cache entry has expired.
+  base::RunLoop second_run_loop;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+                     "known-good", base::TimeDelta::FromSeconds(32LL),
+                     base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                                    base::Unretained(this),
+                                    second_run_loop.QuitClosure())));
+  second_run_loop.Run();
+  ASSERT_EQ(fetched_results_.size(), 2ULL);
+  // We don't capture the time right Now() because the above Fetch()
+  // should have replied with local contents, fetched at time_zero.
+
+  // Advance clock to T+32.
+  AdvanceClock(base::TimeDelta::FromSeconds(1));
+
+  // This third Fetch() will be given the same |expiration| as ever.
+  // The two previous calls to AdvanceClock() will have moved the time
+  // beyond the staleness threshold, though, so this Fetch() will be
+  // networked.
+  base::RunLoop third_run_loop;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+                     "known-good",
+                     // Entry fetched at T+0 is now stale at T+32.
+                     base::TimeDelta::FromSeconds(32LL),
+                     base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                                    base::Unretained(this),
+                                    third_run_loop.QuitClosure())));
+  third_run_loop.Run();
+  ASSERT_EQ(fetched_results_.size(), 3ULL);
+  const base::Time time_of_third_fetch = clock_.Now();
+
+  EXPECT_THAT(fetched_results_,
+              testing::ElementsAre(
+                  FetchResultEquals(PrinterConfigCache::FetchResult::Success(
+                      "known-good", "yakisaba", time_zero)),
+                  FetchResultEquals(PrinterConfigCache::FetchResult::Success(
+                      "known-good", "yakisaba", time_zero)),
+                  FetchResultEquals(PrinterConfigCache::FetchResult::Success(
+                      "known-good", "yakisaba", time_of_third_fetch))));
+}
+
+// Tests that we can Drop() locally cached contents.
+TEST_F(PrinterConfigCacheTest, DropLocalContents) {
+  base::RunLoop first_run_loop;
+
+  // Fetches the "known-good" resource with its stock contents.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+                     "known-good", base::TimeDelta::FromSeconds(604800LL),
+                     base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                                    base::Unretained(this),
+                                    first_run_loop.QuitClosure())));
+  first_run_loop.Run();
+
+  // Drops that which we just fetched. This isn't immediately externally
+  // visible, but its effects will soon be made apparent.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&PrinterConfigCache::Drop,
+                                base::Unretained(cache_.get()), "known-good"));
+
+  // Mutates the contents served for the "known-good" resource.
+  loader_factory_.AddResponse(kKnownGoodResourceURL, "ultimate dogeza");
+
+  // Fetches the "known-good" resource anew with a wide timeout.
+  // This is where the side effect of the prior Drop() call manifests:
+  // the "known-good" resource is no longer cached, so not even a wide
+  // timeout will spare us a networked fetch.
+  base::RunLoop second_run_loop;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
+                     "known-good", base::TimeDelta::FromSeconds(18748800LL),
+                     base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
+                                    base::Unretained(this),
+                                    second_run_loop.QuitClosure())));
+  second_run_loop.Run();
+
+  // We detect the networked fetch to by observing mutated
+  // contents.
+  ASSERT_EQ(fetched_results_.size(), 2ULL);
+  EXPECT_THAT(
+      fetched_results_,
+      testing::ElementsAre(
+          TimeInsensitiveFetchResultEquals(
+              PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
+                                                       kUnusedTimeOfFetch)),
+          TimeInsensitiveFetchResultEquals(
+              PrinterConfigCache::FetchResult::Success(
+                  "known-good", "ultimate dogeza", kUnusedTimeOfFetch))));
+}
+
+}  // namespace
+}  // namespace chromeos
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/animation/Interpolators.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/animation/Interpolators.java
index 059785a..6ee034cd 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/animation/Interpolators.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/animation/Interpolators.java
@@ -4,13 +4,14 @@
 
 package org.chromium.components.browser_ui.widget.animation;
 
-import android.support.v4.view.animation.FastOutLinearInInterpolator;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.LinearInterpolator;
 
+import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
+import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
+import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
+
 /** Reference to one of each standard interpolator to avoid allocations. */
 public class Interpolators {
     public static final AccelerateInterpolator ACCELERATE_INTERPOLATOR =
diff --git a/components/crash/core/common/crash_key.h b/components/crash/core/common/crash_key.h
index 700ad3c..9d193ea 100644
--- a/components/crash/core/common/crash_key.h
+++ b/components/crash/core/common/crash_key.h
@@ -19,6 +19,9 @@
 // Annotation interface. Because not all platforms use Crashpad yet, a
 // source-compatible interface is provided on top of the older Breakpad
 // storage mechanism.
+//
+// See https://cs.chromium.org/chromium/src/docs/debugging_with_crash_keys.md
+// for more information on using this.
 #if BUILDFLAG(USE_CRASHPAD_ANNOTATION) || BUILDFLAG(USE_COMBINED_ANNOTATIONS)
 #include "third_party/crashpad/crashpad/client/annotation.h"  // nogncheck
 #endif
diff --git a/components/download/internal/common/BUILD.gn b/components/download/internal/common/BUILD.gn
index 4f322c44..eba9ab1 100644
--- a/components/download/internal/common/BUILD.gn
+++ b/components/download/internal/common/BUILD.gn
@@ -101,11 +101,15 @@
 
 if (is_android) {
   android_library("internal_java") {
-    sources = [ "android/java/src/org/chromium/components/download/DownloadCollectionBridge.java" ]
+    sources = [
+      "android/java/src/org/chromium/components/download/DownloadCollectionBridge.java",
+      "android/java/src/org/chromium/components/download/DownloadDelegate.java",
+    ]
 
     deps = [
       "//base:base_java",
       "//base:jni_java",
+      "//third_party/android_provider:android_provider_java",
     ]
     annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   }
diff --git a/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java b/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java
index 11bfaeb..a81973b 100644
--- a/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java
+++ b/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadCollectionBridge.java
@@ -4,26 +4,59 @@
 
 package org.chromium.components.download;
 
+import android.annotation.TargetApi;
 import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
 import android.net.Uri;
+import android.os.Build;
+import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
+import android.provider.BaseColumns;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Downloads;
+import android.provider.MediaStore.MediaColumns;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
 
+import androidx.annotation.NonNull;
+
+import org.chromium.base.BuildInfo;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.third_party.android.provider.MediaStoreUtils;
+import org.chromium.third_party.android.provider.MediaStoreUtils.PendingParams;
+import org.chromium.third_party.android.provider.MediaStoreUtils.PendingSession;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
 
 /**
  * Helper class for publishing download files to the public download collection.
  */
 @JNINamespace("download")
 public class DownloadCollectionBridge {
-    // Singleton instance that allows embedders to replace their implementation.
-    private static DownloadCollectionBridge sDownloadCollectionBridge;
     private static final String TAG = "DownloadCollection";
-    // Guards access to sDownloadCollectionBridge.
-    private static final Object sLock = new Object();
+
+    // File name pattern to be used when media store has too many duplicates. This matches
+    // that of download_path_reservation_tracker.cc.
+    private static final String FILE_NAME_PATTERN = "yyyy-MM-dd'T'HHmmss.SSS";
+
+    private static final List<String> COMMON_DOUBLE_EXTENSIONS =
+            new ArrayList<String>(Arrays.asList("tar.gz", "tar.z", "tar.bz2", "tar.bz", "user.js"));
+
+    private static DownloadDelegate sDownloadDelegate = new DownloadDelegate();
 
     /**
      *  Class representing the Uri and display name pair for downloads.
@@ -49,127 +82,13 @@
     }
 
     /**
-     * Return getDownloadCollectionBridge singleton.
+     * Sets the DownloadDelegate to be used for utility methods.
+     * TODO(qinmin): remove this method once we moved all the utility methods into
+     * components/.
+     * @param downloadDelegate The new delegate to be used.
      */
-    public static DownloadCollectionBridge getDownloadCollectionBridge() {
-        synchronized (sLock) {
-            if (sDownloadCollectionBridge == null) {
-                sDownloadCollectionBridge = new DownloadCollectionBridge();
-            }
-        }
-        return sDownloadCollectionBridge;
-    }
-
-    /**
-     * Sets the singlton object to use later.
-     */
-    public static void setDownloadCollectionBridge(DownloadCollectionBridge bridge) {
-        synchronized (sLock) {
-            sDownloadCollectionBridge = bridge;
-        }
-    }
-
-    /**
-     * Returns whether a download needs to be published.
-     * @param filePath File path of the download.
-     * @return True if the download needs to be published, or false otherwise.
-     */
-    public boolean needToPublishDownload(final String filePath) {
-        return false;
-    }
-
-    /**
-     * Creates a pending session for download to be written into.
-     * @param fileName Name of the file.
-     * @param mimeType Mime type of the file.
-     * @param originalUrl Originating URL of the download.
-     * @param referrer Referrer of the download.
-     * @return Uri created for the pending session.
-     */
-    protected Uri createPendingSession(final String fileName, final String mimeType,
-            final String originalUrl, final String referrer) {
-        return null;
-    }
-
-    /**
-     * Copy file content from a source file to the pending Uri.
-     * @param sourcePath File content to be copied from.
-     * @param pendingUri Destination Uri to be copied to.
-     * @return true on success, or false otherwise.
-     */
-    protected boolean copyFileToPendingUri(final String sourcePath, final String pendingUri) {
-        return false;
-    }
-
-    /**
-     * Abandon the the intermediate Uri.
-     * @param pendingUri Intermediate Uri that is going to be deleted.
-     */
-    protected void abandonPendingUri(final String pendingUri) {}
-
-    /**
-     * Publish a completed download to public repository.
-     * @param pendingUri Pending uri to publish.
-     * @return Uri of the published file.
-     */
-    protected Uri publishCompletedDownload(final String pendingUri) {
-        return null;
-    }
-
-    /**
-     * Gets the content URI of the download that has the given file name.
-     * @param pendingUri name of the file.
-     * @return Uri of the download with the given display name.
-     */
-    public Uri getDownloadUriForFileName(final String fileName) {
-        return null;
-    }
-
-    /**
-     * Renames a download Uri with a display name.
-     * @param downloadUri Uri of the download.
-     * @param displayName New display name for the download.
-     * @return whether rename was successful.
-     */
-    protected boolean rename(final String downloadUri, final String displayName) {
-        return false;
-    }
-
-    /**
-     * @return  Whether download display names needs to be retrieved.
-     */
-    protected boolean needToGetDisplayNames() {
-        return false;
-    }
-
-    /**
-     * Gets the display names for all downloads
-     * @return an array of download Uri and display name pair.
-     */
-    protected DisplayNameInfo[] getDisplayNames() {
-        return null;
-    }
-
-    /**
-     * @return whether download collection is supported.
-     */
-    protected boolean isDownloadCollectionSupported() {
-        return false;
-    }
-
-    /**
-     *  Refreshes the expiration date so the unpublished download won't get abandoned.
-     *  @param intermediateUri The intermediate Uri that is not yet published.
-     */
-    protected void refreshExpirationDate(final String intermediateUri) {}
-
-    /**
-     * Gets the display name for a download.
-     * @param downloadUri Uri of the download.
-     * @return the display name of the download.
-     */
-    protected String getDisplayNameForUri(final String downloadUri) {
-        return null;
+    public static void setDownloadDelegate(DownloadDelegate downloadDelegate) {
+        sDownloadDelegate = downloadDelegate;
     }
 
     /**
@@ -183,8 +102,20 @@
     @CalledByNative
     public static String createIntermediateUriForPublish(final String fileName,
             final String mimeType, final String originalUrl, final String referrer) {
-        Uri uri = getDownloadCollectionBridge().createPendingSession(
-                fileName, mimeType, originalUrl, referrer);
+        Uri uri = createPendingSessionInternal(fileName, mimeType, originalUrl, referrer);
+        if (uri != null) return uri.toString();
+
+        // If there are too many duplicates on the same file name, createPendingSessionInternal()
+        // will return null. Generate a new file name with timestamp.
+        SimpleDateFormat sdf = new SimpleDateFormat(FILE_NAME_PATTERN, Locale.getDefault());
+        // Remove the extension first.
+        String baseName = getBaseName(fileName);
+        String extension = fileName.substring(baseName.length());
+        StringBuilder sb = new StringBuilder(baseName);
+        sb.append(" - ");
+        sb.append(sdf.format(new Date()));
+        sb.append(extension);
+        uri = createPendingSessionInternal(sb.toString(), mimeType, originalUrl, referrer);
         return uri == null ? null : uri.toString();
     }
 
@@ -194,8 +125,13 @@
      * @return True if the download needs to be published, or false otherwise.
      */
     @CalledByNative
-    private static boolean shouldPublishDownload(final String filePath) {
-        return getDownloadCollectionBridge().needToPublishDownload(filePath);
+    public static boolean shouldPublishDownload(final String filePath) {
+        if (isAtLeastQ()) {
+            if (filePath == null) return false;
+            // Only need to publish downloads that are on primary storage.
+            return !sDownloadDelegate.isDownloadOnSDCard(filePath);
+        }
+        return false;
     }
 
     /**
@@ -205,9 +141,21 @@
      * @return True on success, or false otherwise.
      */
     @CalledByNative
+    @TargetApi(29)
     public static boolean copyFileToIntermediateUri(
             final String sourcePath, final String destinationUri) {
-        return getDownloadCollectionBridge().copyFileToPendingUri(sourcePath, destinationUri);
+        try {
+            PendingSession session = openPendingUri(destinationUri);
+            OutputStream out = session.openOutputStream();
+            InputStream in = new FileInputStream(sourcePath);
+            FileUtils.copy(in, out);
+            in.close();
+            out.close();
+            return true;
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to copy content to pending Uri.", e);
+        }
+        return false;
     }
 
     /**
@@ -216,7 +164,8 @@
      */
     @CalledByNative
     public static void deleteIntermediateUri(final String uri) {
-        getDownloadCollectionBridge().abandonPendingUri(uri);
+        PendingSession session = openPendingUri(uri);
+        session.abandon();
     }
 
     /**
@@ -226,8 +175,34 @@
      */
     @CalledByNative
     public static String publishDownload(final String intermediateUri) {
-        Uri uri = getDownloadCollectionBridge().publishCompletedDownload(intermediateUri);
-        return uri == null ? null : uri.toString();
+        // Android Q's MediaStore.Downloads has an issue that the custom mime type which is not
+        // supported by MimeTypeMap is overridden to "application/octet-stream" when publishing.
+        // To deal with this issue we set the mime type again after publishing.
+        // See crbug.com/1010829 for more details.
+        ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver();
+        String mimeType = null;
+        Cursor cursor = null;
+        try {
+            cursor = resolver.query(Uri.parse(intermediateUri),
+                    new String[] {MediaColumns.MIME_TYPE}, null, null, null);
+            if (cursor != null && cursor.getCount() != 0 && cursor.moveToNext()) {
+                mimeType = cursor.getString(cursor.getColumnIndex(MediaColumns.MIME_TYPE));
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to get mimeType.", e);
+        } finally {
+            if (cursor != null) cursor.close();
+        }
+
+        PendingSession session = openPendingUri(intermediateUri);
+        Uri publishedUri = session.publish();
+
+        if (!TextUtils.isEmpty(mimeType)) {
+            final ContentValues updateValues = new ContentValues();
+            updateValues.put(MediaColumns.MIME_TYPE, mimeType);
+            resolver.update(publishedUri, updateValues, null, null);
+        }
+        return publishedUri.toString();
     }
 
     /**
@@ -241,7 +216,10 @@
             ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver();
             ParcelFileDescriptor pfd =
                     resolver.openFileDescriptor(Uri.parse(intermediateUri), "rw");
-            getDownloadCollectionBridge().refreshExpirationDate(intermediateUri);
+            ContentValues updateValues = new ContentValues();
+            updateValues.put("date_expires", getNewExpirationTime());
+            ContextUtils.getApplicationContext().getContentResolver().update(
+                    Uri.parse(intermediateUri), updateValues, null, null);
             return pfd.detachFd();
         } catch (Exception e) {
             Log.e(TAG, "Cannot open intermediate Uri.", e);
@@ -250,12 +228,13 @@
     }
 
     /**
+     * Check if a download with the same name already exists.
+     * @param fileName The name of the file to check.
      * @return whether a download with the file name exists.
      */
     @CalledByNative
     private static boolean fileNameExists(final String fileName) {
-        Uri uri = getDownloadCollectionBridge().getDownloadUriForFileName(fileName);
-        return uri != null;
+        return getDownloadUriForFileName(fileName) != null;
     }
 
     /**
@@ -266,7 +245,12 @@
      */
     @CalledByNative
     private static boolean renameDownloadUri(final String downloadUri, final String displayName) {
-        return getDownloadCollectionBridge().rename(downloadUri, displayName);
+        final ContentValues updateValues = new ContentValues();
+        Uri uri = Uri.parse(downloadUri);
+        updateValues.put(MediaColumns.DISPLAY_NAME, displayName);
+        return ContextUtils.getApplicationContext().getContentResolver().update(
+                       uri, updateValues, null, null)
+                == 1;
     }
 
     /**
@@ -274,7 +258,7 @@
      */
     @CalledByNative
     private static boolean needToRetrieveDisplayNames() {
-        return getDownloadCollectionBridge().needToGetDisplayNames();
+        return isAtLeastQ();
     }
 
     /**
@@ -282,15 +266,63 @@
      * @return an array of download Uri and display name pair.
      */
     @CalledByNative
+    @TargetApi(29)
     private static DisplayNameInfo[] getDisplayNamesForDownloads() {
-        return getDownloadCollectionBridge().getDisplayNames();
+        ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver();
+        Cursor cursor = null;
+        try {
+            Uri uri = Downloads.EXTERNAL_CONTENT_URI;
+            cursor = resolver.query(MediaStore.setIncludePending(uri),
+                    new String[] {BaseColumns._ID, MediaColumns.DISPLAY_NAME}, null, null, null);
+            if (cursor == null || cursor.getCount() == 0) return null;
+            List<DisplayNameInfo> infos = new ArrayList<DisplayNameInfo>();
+            while (cursor.moveToNext()) {
+                String displayName =
+                        cursor.getString(cursor.getColumnIndex(MediaColumns.DISPLAY_NAME));
+                Uri downloadUri = ContentUris.withAppendedId(
+                        uri, cursor.getInt(cursor.getColumnIndex(BaseColumns._ID)));
+                infos.add(new DisplayNameInfo(downloadUri.toString(), displayName));
+            }
+            return infos.toArray(new DisplayNameInfo[0]);
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to get display names for downloads.", e);
+        } finally {
+            if (cursor != null) cursor.close();
+        }
+        return null;
     }
 
     /**
      * @return whether download collection is supported.
      */
     public static boolean supportsDownloadCollection() {
-        return getDownloadCollectionBridge().isDownloadCollectionSupported();
+        return isAtLeastQ();
+    }
+
+    /**
+     * Gets the content URI of the download that has the given file name.
+     * @param pendingUri name of the file.
+     * @return Uri of the download with the given display name.
+     */
+    @TargetApi(29)
+    public static Uri getDownloadUriForFileName(String fileName) {
+        Cursor cursor = null;
+        try {
+            Uri uri = Downloads.EXTERNAL_CONTENT_URI;
+            cursor = ContextUtils.getApplicationContext().getContentResolver().query(
+                    MediaStore.setIncludePending(uri), new String[] {BaseColumns._ID},
+                    "_display_name LIKE ?1", new String[] {fileName}, null);
+            if (cursor == null) return null;
+            if (cursor.moveToNext()) {
+                return ContentUris.withAppendedId(
+                        uri, cursor.getInt(cursor.getColumnIndex(BaseColumns._ID)));
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to check file name existence.", e);
+        } finally {
+            if (cursor != null) cursor.close();
+        }
+        return null;
     }
 
     /**
@@ -300,6 +332,93 @@
         return DownloadCollectionBridgeJni.get().getExpirationDurationInDays();
     }
 
+    private static boolean isAtLeastQ() {
+        return BuildInfo.isAtLeastQ() || Build.VERSION.SDK_INT >= 29;
+    }
+
+    /**
+     * Helper method to create a pending session for download to be written into.
+     * @param fileName Name of the file.
+     * @param mimeType Mime type of the file.
+     * @param originalUrl Originating URL of the download.
+     * @param referrer Referrer of the download.
+     * @return Uri created for the pending session, or null if failed.
+     */
+    private static Uri createPendingSessionInternal(final String fileName, final String mimeType,
+            final String originalUrl, final String referrer) {
+        PendingParams pendingParams =
+                createPendingParams(fileName, mimeType, originalUrl, referrer);
+        pendingParams.setExpirationTime(getNewExpirationTime());
+        try {
+            return MediaStoreUtils.createPending(
+                    ContextUtils.getApplicationContext(), pendingParams);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * Helper method to create PendingParams needed for PendingSession creation.
+     * @param fileName Name of the file.
+     * @param mimeType Mime type of the file.
+     * @param originalUrl Originating URL of the download.
+     * @param referrer Referrer of the download.
+     * @return PendingParams needed for creating the PendingSession.
+     */
+    @TargetApi(29)
+    private static PendingParams createPendingParams(final String fileName, final String mimeType,
+            final String originalUrl, final String referrer) {
+        Uri downloadsUri = Downloads.EXTERNAL_CONTENT_URI;
+        String newMimeType =
+                sDownloadDelegate.remapGenericMimeType(mimeType, originalUrl, fileName);
+        PendingParams pendingParams = new PendingParams(downloadsUri, fileName, newMimeType);
+        Uri originalUri = sDownloadDelegate.parseOriginalUrl(originalUrl);
+        Uri referrerUri = TextUtils.isEmpty(referrer) ? null : Uri.parse(referrer);
+        pendingParams.setDownloadUri(originalUri);
+        pendingParams.setRefererUri(referrerUri);
+        return pendingParams;
+    }
+
+    /**
+     *  Gets the base name, without extension, from a file name.
+     *  TODO(qinmin): move this into a common utility class.
+     *  @param fileName Name of the file.
+     *  @return Base name of the file.
+     */
+    private static String getBaseName(final String fileName) {
+        for (String extension : COMMON_DOUBLE_EXTENSIONS) {
+            if (fileName.endsWith(extension)) {
+                String name = fileName.substring(0, fileName.length() - extension.length());
+                // remove the "." at the end.
+                if (name.endsWith(".")) {
+                    return name.substring(0, name.length() - 1);
+                }
+            }
+        }
+        int index = fileName.lastIndexOf('.');
+        if (index == -1) {
+            return fileName;
+        } else {
+            return fileName.substring(0, index);
+        }
+    }
+
+    private static @NonNull PendingSession openPendingUri(final String pendingUri) {
+        return MediaStoreUtils.openPending(
+                ContextUtils.getApplicationContext(), Uri.parse(pendingUri));
+    }
+
+    /**
+     * Helper method to generate a new expiration epoch time in seconds.
+     * @return Epoch time value in seconds for the download to expire.
+     */
+    private static long getNewExpirationTime() {
+        return (System.currentTimeMillis()
+                       + DownloadCollectionBridge.getExpirationDurationInDays()
+                               * DateUtils.DAY_IN_MILLIS)
+                / 1000;
+    }
+
     /**
      * Gets the display name for a download.
      * @param downloadUri Uri of the download.
@@ -307,7 +426,21 @@
      */
     @CalledByNative
     private static String getDisplayName(final String downloadUri) {
-        return getDownloadCollectionBridge().getDisplayNameForUri(downloadUri);
+        ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver();
+        Cursor cursor = null;
+        try {
+            cursor = resolver.query(Uri.parse(downloadUri),
+                    new String[] {MediaColumns.DISPLAY_NAME}, null, null, null);
+            if (cursor == null || cursor.getCount() == 0) return null;
+            if (cursor.moveToNext()) {
+                return cursor.getString(cursor.getColumnIndex(MediaColumns.DISPLAY_NAME));
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to get display name for download.", e);
+        } finally {
+            if (cursor != null) cursor.close();
+        }
+        return null;
     }
 
     @NativeMethods
diff --git a/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadDelegate.java b/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadDelegate.java
new file mode 100644
index 0000000..00a38da
--- /dev/null
+++ b/components/download/internal/common/android/java/src/org/chromium/components/download/DownloadDelegate.java
@@ -0,0 +1,47 @@
+// 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.download;
+
+import android.net.Uri;
+
+/**
+ * Helper class for providering some helper method needed by DownloadCollectionBridge.
+ */
+public class DownloadDelegate {
+    public DownloadDelegate() {}
+
+    /**
+     * If the given MIME type is null, or one of the "generic" types (text/plain
+     * or application/octet-stream) map it to a type that Android can deal with.
+     * If the given type is not generic, return it unchanged.
+     *
+     * @param mimeType MIME type provided by the server.
+     * @param url URL of the data being loaded.
+     * @param filename file name obtained from content disposition header
+     * @return The MIME type that should be used for this data.
+     */
+    public String remapGenericMimeType(String mimeType, String url, String filename) {
+        return mimeType;
+    }
+
+    /**
+     * Parses an originating URL string and returns a valid Uri that can be inserted into
+     * DownloadManager. The returned Uri has to be null or non-empty http(s) scheme.
+     * @param originalUrl String representation of the originating URL.
+     * @return A valid Uri that can be accepted by DownloadManager.
+     */
+    public Uri parseOriginalUrl(String originalUrl) {
+        return Uri.parse(originalUrl);
+    }
+
+    /**
+     * Returns whether the downloaded file path is on an external SD card.
+     * @param filePath The download file path.
+     * @return Whether download is on external sd card.
+     */
+    public boolean isDownloadOnSDCard(String filePath) {
+        return false;
+    }
+}
diff --git a/components/image_fetcher/core/image_data_fetcher.cc b/components/image_fetcher/core/image_data_fetcher.cc
index 00e8951..01f4be8 100644
--- a/components/image_fetcher/core/image_data_fetcher.cc
+++ b/components/image_fetcher/core/image_data_fetcher.cc
@@ -114,10 +114,7 @@
       DVLOG(0) << "Failed to parse data url";
     }
 
-    // Post a task to maintain our guarantee that the call won't be called
-    // synchronously.
-    base::PostTask(FROM_HERE, BindOnce(std::move(callback), std::move(data),
-                                       RequestMetadata()));
+    std::move(callback).Run(std::move(data), RequestMetadata());
     return;
   }
 
diff --git a/components/image_fetcher/core/image_data_fetcher.h b/components/image_fetcher/core/image_data_fetcher.h
index 7f91ef77..82781f0 100644
--- a/components/image_fetcher/core/image_data_fetcher.h
+++ b/components/image_fetcher/core/image_data_fetcher.h
@@ -42,7 +42,7 @@
 
   // Fetches the raw image bytes from the given |image_url| and calls the given
   // |callback|. The callback is run even if fetching the URL fails. In case
-  // of an error an empty string is passed to the callback. Won't return
+  // of an error an empty string is passed to the callback. May return
   // synchronously.
   void FetchImageData(const GURL& image_url,
                       ImageDataFetcherCallback callback,
diff --git a/components/image_fetcher/core/image_data_fetcher_unittest.cc b/components/image_fetcher/core/image_data_fetcher_unittest.cc
index b5806c50..6b65801 100644
--- a/components/image_fetcher/core/image_data_fetcher_unittest.cc
+++ b/components/image_fetcher/core/image_data_fetcher_unittest.cc
@@ -60,7 +60,7 @@
                void(const std::string&, const RequestMetadata&));
 
  protected:
-  base::test::TaskEnvironment task_environment_;
+  base::test::SingleThreadTaskEnvironment task_environment_;
   base::HistogramTester histogram_tester_;
 
   network::TestURLLoaderFactory test_url_loader_factory_;
@@ -115,17 +115,16 @@
       "wn4GBgYGJAQoAHhgCAh6X4CYAAAAASUVORK5CYII=";
   std::string data_url = "data:image/png;base64," + data;
 
+  RequestMetadata expected_metadata;
+  std::string expected;
+  base::Base64Decode(data, &expected);
+  EXPECT_CALL(*this, OnImageDataFetched(expected, expected_metadata));
+
   image_data_fetcher_.FetchImageData(
       GURL(data_url),
       base::BindOnce(&ImageDataFetcherTest::OnImageDataFetched,
                      base::Unretained(this)),
       ImageFetcherParams(TRAFFIC_ANNOTATION_FOR_TESTS, kTestUmaClientName));
-
-  RequestMetadata expected_metadata;
-  std::string expected;
-  base::Base64Decode(data, &expected);
-  EXPECT_CALL(*this, OnImageDataFetched(expected, expected_metadata));
-  base::RunLoop().RunUntilIdle();
 }
 
 TEST_F(ImageDataFetcherTest, FetchImageDataTrafficAnnotationOnly) {
diff --git a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
index 636d2f6..7e537bc1 100644
--- a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
+++ b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
@@ -89,10 +89,6 @@
     "InitiatingProcess";
 const char kHistogramFirstMeaningfulPaint[] =
     "PageLoad.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint";
-const char kHistogramLargestImagePaint[] =
-    "PageLoad.Experimental.PaintTiming.NavigationToLargestImagePaint";
-const char kHistogramLargestTextPaint[] =
-    "PageLoad.Experimental.PaintTiming.NavigationToLargestTextPaint";
 const char kHistogramLargestContentfulPaint[] =
     "PageLoad.PaintTiming.NavigationToLargestContentfulPaint";
 const char kHistogramLargestContentfulPaintContentType[] =
@@ -719,26 +715,6 @@
   }
 
   const page_load_metrics::ContentfulPaintTimingInfo&
-      main_frame_largest_image_paint =
-          largest_contentful_paint_handler_.MainFrameLargestImagePaint();
-  if (main_frame_largest_image_paint.ContainsValidTime() &&
-      WasStartedInForegroundOptionalEventInForeground(
-          main_frame_largest_image_paint.Time(), GetDelegate())) {
-    PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestImagePaint,
-                        main_frame_largest_image_paint.Time().value());
-  }
-
-  const page_load_metrics::ContentfulPaintTimingInfo&
-      main_frame_largest_text_paint =
-          largest_contentful_paint_handler_.MainFrameLargestTextPaint();
-  if (main_frame_largest_text_paint.ContainsValidTime() &&
-      WasStartedInForegroundOptionalEventInForeground(
-          main_frame_largest_text_paint.Time(), GetDelegate())) {
-    PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestTextPaint,
-                        main_frame_largest_text_paint.Time().value());
-  }
-
-  const page_load_metrics::ContentfulPaintTimingInfo&
       main_frame_largest_contentful_paint =
           largest_contentful_paint_handler_.MainFrameLargestContentfulPaint();
   if (main_frame_largest_contentful_paint.ContainsValidTime() &&
diff --git a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h
index 3af7079..7f53f40 100644
--- a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h
+++ b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h
@@ -28,8 +28,6 @@
 extern const char kHistogramLoad[];
 extern const char kHistogramFirstContentfulPaint[];
 extern const char kHistogramFirstMeaningfulPaint[];
-extern const char kHistogramLargestImagePaint[];
-extern const char kHistogramLargestTextPaint[];
 extern const char kHistogramLargestContentfulPaint[];
 extern const char kHistogramLargestContentfulPaintContentType[];
 extern const char kHistogramLargestContentfulPaintMainFrame[];
diff --git a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
index 5f99ae32..b6b963e 100644
--- a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
+++ b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer_unittest.cc
@@ -626,26 +626,6 @@
       internal::FIRST_MEANINGFUL_PAINT_RECORDED, 1);
 }
 
-TEST_F(CorePageLoadMetricsObserverTest, LargestImagePaint) {
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  timing.navigation_start = base::Time::FromDoubleT(1);
-  // Pick a value that lines up with a histogram bucket.
-  timing.paint_timing->largest_image_paint =
-      base::TimeDelta::FromMilliseconds(4780);
-  timing.paint_timing->largest_image_paint_size = 10u;
-  PopulateRequiredTimingFields(&timing);
-
-  NavigateAndCommit(GURL(kDefaultTestUrl));
-  tester()->SimulateTimingUpdate(timing);
-  // Navigate again to force histogram recording.
-  NavigateAndCommit(GURL(kDefaultTestUrl2));
-
-  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
-                  internal::kHistogramLargestImagePaint),
-              testing::ElementsAre(base::Bucket(4780, 1)));
-}
-
 TEST_F(CorePageLoadMetricsObserverTest, LargestImageLoading) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
@@ -665,15 +645,9 @@
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  tester()->histogram_tester().ExpectTotalCount(
-      internal::kHistogramLargestImagePaint, 0);
   // The image was larger so LCP should NOT be reported.
   tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLargestContentfulPaint, 0);
-
-  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
-                  internal::kHistogramLargestTextPaint),
-              testing::ElementsAre(base::Bucket(4780, 1)));
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, LargestImageLoadingSmallerThanText) {
@@ -695,12 +669,6 @@
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  tester()->histogram_tester().ExpectTotalCount(
-      internal::kHistogramLargestImagePaint, 0);
-
-  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
-                  internal::kHistogramLargestTextPaint),
-              testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
@@ -1092,95 +1060,6 @@
           1)));
 }
 
-TEST_F(CorePageLoadMetricsObserverTest,
-       LargestImagePaint_DiscardBackgroundResult) {
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  PopulateRequiredTimingFields(&timing);
-
-  NavigateAndCommit(GURL(kDefaultTestUrl));
-  web_contents()->WasHidden();
-  // This event happens after first background, so it will be discarded.
-  timing.paint_timing->largest_image_paint = base::Time::Now() - base::Time();
-  tester()->SimulateTimingUpdate(timing);
-  // Navigate again to force histogram recording.
-  NavigateAndCommit(GURL(kDefaultTestUrl2));
-
-  tester()->histogram_tester().ExpectTotalCount(
-      internal::kHistogramLargestImagePaint, 0);
-}
-
-TEST_F(CorePageLoadMetricsObserverTest, LargestImagePaint_ReportLastCandidate) {
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-
-  NavigateAndCommit(GURL(kDefaultTestUrl));
-  timing.navigation_start = base::Time::FromDoubleT(1);
-
-  timing.paint_timing->largest_image_paint =
-      base::TimeDelta::FromMilliseconds(1000);
-  timing.paint_timing->largest_image_paint_size = 10u;
-  PopulateRequiredTimingFields(&timing);
-  tester()->SimulateTimingUpdate(timing);
-
-  timing.paint_timing->largest_image_paint =
-      base::TimeDelta::FromMilliseconds(4780);
-  timing.paint_timing->largest_image_paint_size = 5u;
-  PopulateRequiredTimingFields(&timing);
-  tester()->SimulateTimingUpdate(timing);
-  // Navigate again to force histogram recording.
-  NavigateAndCommit(GURL(kDefaultTestUrl2));
-
-  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
-                  internal::kHistogramLargestImagePaint),
-              testing::ElementsAre(base::Bucket(4780, 1)));
-}
-
-TEST_F(CorePageLoadMetricsObserverTest, ReportLastNullCandidate) {
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-
-  NavigateAndCommit(GURL(kDefaultTestUrl));
-  timing.navigation_start = base::Time::FromDoubleT(1);
-
-  timing.paint_timing->largest_image_paint =
-      base::TimeDelta::FromMilliseconds(1000);
-  timing.paint_timing->largest_image_paint_size = 10u;
-
-  PopulateRequiredTimingFields(&timing);
-  tester()->SimulateTimingUpdate(timing);
-
-  timing.paint_timing->largest_image_paint = base::Optional<base::TimeDelta>();
-  timing.paint_timing->largest_image_paint_size = 0;
-  PopulateRequiredTimingFields(&timing);
-  tester()->SimulateTimingUpdate(timing);
-  // Navigate again to force histogram recording.
-  NavigateAndCommit(GURL(kDefaultTestUrl2));
-
-  tester()->histogram_tester().ExpectTotalCount(
-      internal::kHistogramLargestImagePaint, 0);
-}
-
-TEST_F(CorePageLoadMetricsObserverTest, LargestTextPaint) {
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  timing.navigation_start = base::Time::FromDoubleT(1);
-  // Pick a value that lines up with a histogram bucket.
-  timing.paint_timing->largest_text_paint =
-      base::TimeDelta::FromMilliseconds(4780);
-  timing.paint_timing->largest_text_paint_size = 10u;
-  PopulateRequiredTimingFields(&timing);
-
-  NavigateAndCommit(GURL(kDefaultTestUrl));
-  tester()->SimulateTimingUpdate(timing);
-  // Navigate again to force histogram recording.
-  NavigateAndCommit(GURL(kDefaultTestUrl2));
-
-  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
-                  internal::kHistogramLargestTextPaint),
-              testing::ElementsAre(base::Bucket(4780, 1)));
-}
-
 TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_NoTextOrImage) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
diff --git a/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h b/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
index e2912b2..9a0831a0 100644
--- a/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
+++ b/components/page_load_metrics/browser/observers/largest_contentful_paint_handler.h
@@ -93,12 +93,6 @@
   const ContentfulPaintTimingInfo& SubframesLargestContentfulPaint() {
     return subframe_contentful_paint_.MergeTextAndImageTiming();
   }
-  const ContentfulPaintTimingInfo& MainFrameLargestImagePaint() {
-    return main_frame_contentful_paint_.Image();
-  }
-  const ContentfulPaintTimingInfo& MainFrameLargestTextPaint() {
-    return main_frame_contentful_paint_.Text();
-  }
 
   // We merge the candidates from main frame and subframe to get the largest
   // candidate across all frames.
diff --git a/components/safe_browsing/core/BUILD.gn b/components/safe_browsing/core/BUILD.gn
index 86d0bb9..9ef255ed 100644
--- a/components/safe_browsing/core/BUILD.gn
+++ b/components/safe_browsing/core/BUILD.gn
@@ -76,6 +76,7 @@
     "//base",
     "//components/content_settings/core/browser",
     "//components/history/core/browser",
+    "//components/keyed_service/core:core",
     "//components/password_manager/core/browser:browser",
     "//components/safe_browsing/core/common:thread_utils",
     "//components/safe_browsing/core/db:v4_protocol_manager_util",
diff --git a/components/safe_browsing/core/verdict_cache_manager.cc b/components/safe_browsing/core/verdict_cache_manager.cc
index 6ef30d1..64694b3 100644
--- a/components/safe_browsing/core/verdict_cache_manager.cc
+++ b/components/safe_browsing/core/verdict_cache_manager.cc
@@ -366,12 +366,14 @@
     history_service_observer_.Add(history_service);
 }
 
-VerdictCacheManager::~VerdictCacheManager() {
+void VerdictCacheManager::Shutdown() {
   CleanUpExpiredVerdicts();
   history_service_observer_.RemoveAll();
   weak_factory_.InvalidateWeakPtrs();
 }
 
+VerdictCacheManager::~VerdictCacheManager() {}
+
 void VerdictCacheManager::CachePhishGuardVerdict(
     LoginReputationClientRequest::TriggerType trigger_type,
     ReusedPasswordAccountType password_type,
diff --git a/components/safe_browsing/core/verdict_cache_manager.h b/components/safe_browsing/core/verdict_cache_manager.h
index 37464c0..915b6577 100644
--- a/components/safe_browsing/core/verdict_cache_manager.h
+++ b/components/safe_browsing/core/verdict_cache_manager.h
@@ -14,6 +14,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_service_observer.h"
+#include "components/keyed_service/core/keyed_service.h"
 #include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/safe_browsing/core/proto/realtimeapi.pb.h"
 #include "url/gurl.h"
@@ -26,7 +27,8 @@
     LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
 
 // Structure: http://screen/YaNfDRYrcnk.png.
-class VerdictCacheManager : public history::HistoryServiceObserver {
+class VerdictCacheManager : public history::HistoryServiceObserver,
+                            public KeyedService {
  public:
   explicit VerdictCacheManager(
       history::HistoryService* history_service,
@@ -42,6 +44,10 @@
     return weak_factory_.GetWeakPtr();
   }
 
+  // KeyedService:
+  // Called before the actual deletion of the object.
+  void Shutdown() override;
+
   // Stores |verdict| in |content_settings_| based on its |trigger_type|, |url|,
   // reused |password_type|, |verdict| and |receive_time|.
   void CachePhishGuardVerdict(
diff --git a/components/services/storage/public/mojom/service_worker_database.mojom b/components/services/storage/public/mojom/service_worker_database.mojom
index 5191a12f..26ce471a 100644
--- a/components/services/storage/public/mojom/service_worker_database.mojom
+++ b/components/services/storage/public/mojom/service_worker_database.mojom
@@ -55,8 +55,7 @@
   // Not populated until the registration is stored into database.
   int64 resources_total_size_bytes = 0;
 
-  network.mojom.CrossOriginEmbedderPolicyValue cross_origin_embedder_policy =
-      network.mojom.CrossOriginEmbedderPolicyValue.kNone;
+  network.mojom.CrossOriginEmbedderPolicy cross_origin_embedder_policy;
 };
 
 // Represents a service worker script data which is stored in database.
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index bf6a447..cd3ea3c4 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -15,14 +15,8 @@
 
 namespace features {
 
-// Use Skia's readback API instead of GLRendererCopier.
-#if defined(OS_WIN) || defined(OS_LINUX)
 const base::Feature kUseSkiaForGLReadback{"UseSkiaForGLReadback",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
-#else
-const base::Feature kUseSkiaForGLReadback{"UseSkiaForGLReadback",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
 
 // Use the SkiaRenderer.
 #if defined(OS_LINUX) && !(defined(OS_CHROMEOS) || BUILDFLAG(IS_CHROMECAST))
diff --git a/content/browser/devtools/service_worker_devtools_agent_host.cc b/content/browser/devtools/service_worker_devtools_agent_host.cc
index 1901d466..9f3b9a4 100644
--- a/content/browser/devtools/service_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/service_worker_devtools_agent_host.cc
@@ -205,15 +205,13 @@
     return;
   }
   const url::Origin origin = url::Origin::Create(url_);
-  // TODO(https://crbug.com/1039613): Get the COEP value for the service worker
+  // TODO(https://crbug.com/1039613): Get the COEP for the service worker
   // and pass it to each factory bundle.
   auto script_bundle = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
-      rph, worker_route_id_, origin,
-      network::mojom::CrossOriginEmbedderPolicyValue::kNone,
+      rph, worker_route_id_, origin, network::CrossOriginEmbedderPolicy(),
       ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript);
   auto subresource_bundle = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
-      rph, worker_route_id_, origin,
-      network::mojom::CrossOriginEmbedderPolicyValue::kNone,
+      rph, worker_route_id_, origin, network::CrossOriginEmbedderPolicy(),
       ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerSubResource);
 
   if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 799ad94..fd77a87 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1895,10 +1895,6 @@
   // process's channel.
   remote_associated_interfaces_.reset();
 
-  // Ensure that the AssociatedRemote<blink::mojom::LocalFrame> works after a
-  // crash.
-  local_frame_.reset();
-
   // Any termination disablers in content loaded by the new process will
   // be sent again.
   has_before_unload_handler_ = false;
@@ -6012,6 +6008,8 @@
   frame_.reset();
   frame_bindings_control_.reset();
   frame_host_associated_receiver_.reset();
+  local_frame_.reset();
+  local_main_frame_.reset();
   navigation_control_.reset();
   frame_input_handler_.reset();
   find_in_page_.reset();
@@ -6027,6 +6025,7 @@
   sensor_provider_proxy_.reset();
 
   local_frame_host_receiver_.reset();
+  local_main_frame_host_receiver_.reset();
   associated_registry_.reset();
 }
 
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index fbeb817..6f6feb2 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -1045,8 +1045,8 @@
 void RenderFrameHostManager::OnEnforceInsecureRequestPolicy(
     blink::mojom::InsecureRequestPolicy policy) {
   for (const auto& pair : proxy_hosts_) {
-    pair.second->Send(new FrameMsg_EnforceInsecureRequestPolicy(
-        pair.second->GetRoutingID(), policy));
+    pair.second->GetAssociatedRemoteFrame()->EnforceInsecureRequestPolicy(
+        policy);
   }
 }
 
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 8f0e562..9d16815 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -88,21 +88,6 @@
   EXPECT_EQ(expected_focus, focus_message->focused());
 }
 
-// Helper function for strict mixed content checking tests.
-void CheckInsecureRequestPolicyIPC(
-    TestRenderFrameHost* rfh,
-    blink::mojom::InsecureRequestPolicy expected_param,
-    int expected_routing_id) {
-  const IPC::Message* message =
-      rfh->GetProcess()->sink().GetUniqueMessageMatching(
-          FrameMsg_EnforceInsecureRequestPolicy::ID);
-  ASSERT_TRUE(message);
-  EXPECT_EQ(expected_routing_id, message->routing_id());
-  FrameMsg_EnforceInsecureRequestPolicy::Param params;
-  EXPECT_TRUE(FrameMsg_EnforceInsecureRequestPolicy::Read(message, &params));
-  EXPECT_EQ(expected_param, std::get<0>(params));
-}
-
 class RenderFrameHostManagerTestWebUIControllerFactory
     : public WebUIControllerFactory {
  public:
@@ -2914,6 +2899,53 @@
   EXPECT_FALSE(GetPendingFrameHost(manager));
 }
 
+// This class intercepts RenderFrameProxyHost creations, and overrides their
+// respective blink::mojom::RemoteFrame instances.
+class InsecureRequestPolicyProxyObserver {
+ public:
+  InsecureRequestPolicyProxyObserver() {
+    RenderFrameProxyHost::SetCreatedCallbackForTesting(
+        base::BindRepeating(&InsecureRequestPolicyProxyObserver::
+                                RenderFrameProxyHostCreatedCallback,
+                            base::Unretained(this)));
+  }
+  ~InsecureRequestPolicyProxyObserver() {
+    RenderFrameProxyHost::SetCreatedCallbackForTesting(
+        RenderFrameProxyHost::CreatedCallback());
+  }
+  blink::mojom::InsecureRequestPolicy GetRequestPolicy(
+      RenderFrameProxyHost* proxy_host) {
+    return remote_frames_[proxy_host]->enforce_insecure_request_policy();
+  }
+
+ private:
+  // Stub out remote frame mojo binding. Intercepts calls to
+  // EnforceInsecureRequestPolicy and marks the message as received.
+  class RemoteFrame : public content::FakeRemoteFrame {
+   public:
+    explicit RemoteFrame(RenderFrameProxyHost* render_frame_proxy_host) {
+      Init(render_frame_proxy_host->GetRemoteAssociatedInterfacesTesting());
+    }
+
+    void EnforceInsecureRequestPolicy(
+        blink::mojom::InsecureRequestPolicy policy) override {
+      enforce_insecure_request_policy_ = policy;
+    }
+    blink::mojom::InsecureRequestPolicy enforce_insecure_request_policy() {
+      return enforce_insecure_request_policy_;
+    }
+
+   private:
+    blink::mojom::InsecureRequestPolicy enforce_insecure_request_policy_;
+  };
+
+  void RenderFrameProxyHostCreatedCallback(RenderFrameProxyHost* proxy_host) {
+    remote_frames_[proxy_host] = std::make_unique<RemoteFrame>(proxy_host);
+  }
+
+  std::map<RenderFrameProxyHost*, std::unique_ptr<RemoteFrame>> remote_frames_;
+};
+
 // Tests that frame proxies receive updates when a frame's enforcement
 // of insecure request policy changes.
 TEST_P(RenderFrameHostManagerTestWithSiteIsolation,
@@ -2921,6 +2953,7 @@
   const GURL kUrl1("http://www.google.test");
   const GURL kUrl2("http://www.google2.test");
   const GURL kUrl3("http://www.google2.test/foo");
+  InsecureRequestPolicyProxyObserver observer;
 
   contents()->NavigateAndCommit(kUrl1);
 
@@ -2956,9 +2989,9 @@
   RenderFrameProxyHost* proxy_to_child =
       root->render_manager()->GetRenderFrameProxyHost(
           child_host->GetSiteInstance());
-  EXPECT_NO_FATAL_FAILURE(CheckInsecureRequestPolicyIPC(
-      child_host, blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
-      proxy_to_child->GetRoutingID()));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
+            observer.GetRequestPolicy(proxy_to_child));
   EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
             root->current_replication_state().insecure_request_policy);
 
@@ -2975,10 +3008,9 @@
       blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent);
   RenderFrameProxyHost* proxy_to_parent =
       child->GetRenderFrameProxyHost(main_test_rfh()->GetSiteInstance());
-  EXPECT_NO_FATAL_FAILURE(CheckInsecureRequestPolicyIPC(
-      main_test_rfh(),
-      blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
-      proxy_to_parent->GetRoutingID()));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
+            observer.GetRequestPolicy(proxy_to_parent));
   EXPECT_EQ(
       blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent,
       root->child_at(0)->current_replication_state().insecure_request_policy);
@@ -2987,10 +3019,9 @@
   // when the child navigates.
   main_test_rfh()->GetProcess()->sink().ClearMessages();
   NavigationSimulator::NavigateAndCommitFromDocument(kUrl3, child_host);
-  EXPECT_NO_FATAL_FAILURE(CheckInsecureRequestPolicyIPC(
-      main_test_rfh(),
-      blink::mojom::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
-      proxy_to_parent->GetRoutingID()));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(blink::mojom::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
+            observer.GetRequestPolicy(proxy_to_parent));
   EXPECT_EQ(
       blink::mojom::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
       root->child_at(0)->current_replication_state().insecure_request_policy);
diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc
index 620bd68..0fa99dc 100644
--- a/content/browser/ppapi_plugin_process_host.cc
+++ b/content/browser/ppapi_plugin_process_host.cc
@@ -369,7 +369,10 @@
   int flags = plugin_launcher.empty() ? ChildProcessHost::CHILD_ALLOW_SELF :
                                         ChildProcessHost::CHILD_NORMAL;
 #elif defined(OS_MACOSX)
-  int flags = ChildProcessHost::CHILD_PLUGIN;
+  // Flash needs to JIT, but other plugins do not.
+  int flags = permissions_.HasPermission(ppapi::PERMISSION_FLASH)
+                  ? ChildProcessHost::CHILD_PLUGIN
+                  : ChildProcessHost::CHILD_NORMAL;
 #else
   int flags = ChildProcessHost::CHILD_NORMAL;
 #endif
diff --git a/content/browser/renderer_host/OWNERS b/content/browser/renderer_host/OWNERS
index e566b73..36f34e3 100644
--- a/content/browser/renderer_host/OWNERS
+++ b/content/browser/renderer_host/OWNERS
@@ -24,6 +24,7 @@
 # DelegatedFrame*
 per-file *delegated_frame*=fsamuel@chromium.org
 per-file *delegated_frame*=samans@chromium.org
+per-file *delegated_frame*=jonross@chromium.org
 
 # WebSQL
 per-file web_database_*=jsbell@chromium.org
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index d66ff362..c1d5c75a 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -124,7 +124,7 @@
     int embedded_worker_id,
     base::WeakPtr<ServiceWorkerProcessManager> process_manager,
     bool can_use_existing_process,
-    network::mojom::CrossOriginEmbedderPolicyValue cross_origin_embedder_policy,
+    const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
     blink::mojom::EmbeddedWorkerStartParamsPtr params,
     mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient> receiver,
     ServiceWorkerContextCore* context,
@@ -528,10 +528,10 @@
     return skip_recording_startup_time_;
   }
 
-  void Start(blink::mojom::EmbeddedWorkerStartParamsPtr params,
-             network::mojom::CrossOriginEmbedderPolicyValue
-                 cross_origin_embedder_policy,
-             StatusCallback sent_start_callback) {
+  void Start(
+      blink::mojom::EmbeddedWorkerStartParamsPtr params,
+      const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
+      StatusCallback sent_start_callback) {
     DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
     DCHECK(instance_->context_);
     TRACE_EVENT_WITH_FLOW0(
@@ -1037,7 +1037,7 @@
     RenderProcessHost* rph,
     int routing_id,
     const url::Origin& origin,
-    network::mojom::CrossOriginEmbedderPolicyValue cross_origin_embedder_policy,
+    const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
     ContentBrowserClient::URLLoaderFactoryType factory_type) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto factory_bundle =
@@ -1067,9 +1067,7 @@
 
   factory_params->client_security_state =
       network::mojom::ClientSecurityState::New();
-  // TODO(https://crbug.com/1056122): Plumb other fields to support report only
-  // mode.
-  factory_params->client_security_state->cross_origin_embedder_policy.value =
+  factory_params->client_security_state->cross_origin_embedder_policy =
       std::move(cross_origin_embedder_policy);
 
   rph->CreateURLLoaderFactory(std::move(default_factory_receiver),
diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h
index 9801849..8576699 100644
--- a/content/browser/service_worker/embedded_worker_instance.h
+++ b/content/browser/service_worker/embedded_worker_instance.h
@@ -226,8 +226,7 @@
       RenderProcessHost* rph,
       int routing_id,
       const url::Origin& origin,
-      network::mojom::CrossOriginEmbedderPolicyValue
-          cross_origin_embedder_policy,
+      const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
       ContentBrowserClient::URLLoaderFactoryType factory_type);
 
  private:
diff --git a/content/browser/service_worker/service_worker_database.cc b/content/browser/service_worker/service_worker_database.cc
index 9a504aa..27358da 100644
--- a/content/browser/service_worker/service_worker_database.cc
+++ b/content/browser/service_worker/service_worker_database.cc
@@ -1556,17 +1556,30 @@
         static_cast<blink::mojom::ServiceWorkerUpdateViaCache>(value);
   }
 
-  if (data.has_cross_origin_embedder_policy()) {
-    switch (data.cross_origin_embedder_policy()) {
-      case ServiceWorkerRegistrationData::NONE_OR_NOT_EXIST:
-        (*out)->cross_origin_embedder_policy =
-            network::mojom::CrossOriginEmbedderPolicyValue::kNone;
-        break;
-      case ServiceWorkerRegistrationData::REQUIRE_CORP:
-        (*out)->cross_origin_embedder_policy =
-            network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
-        break;
-    }
+  if (data.has_cross_origin_embedder_policy_value()) {
+    (*out)->cross_origin_embedder_policy.value =
+        data.cross_origin_embedder_policy_value() ==
+                ServiceWorkerRegistrationData::NONE_OR_NOT_EXIST
+            ? network::mojom::CrossOriginEmbedderPolicyValue::kNone
+            : network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+  }
+
+  if (data.has_cross_origin_embedder_policy_reporting_endpoint()) {
+    (*out)->cross_origin_embedder_policy.reporting_endpoint =
+        data.cross_origin_embedder_policy_reporting_endpoint();
+  }
+
+  if (data.has_cross_origin_embedder_policy_report_only_value()) {
+    (*out)->cross_origin_embedder_policy.report_only_value =
+        data.cross_origin_embedder_policy_report_only_value() ==
+                ServiceWorkerRegistrationData::NONE_OR_NOT_EXIST
+            ? network::mojom::CrossOriginEmbedderPolicyValue::kNone
+            : network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+  }
+
+  if (data.has_cross_origin_embedder_policy_report_only_reporting_endpoint()) {
+    (*out)->cross_origin_embedder_policy.report_only_reporting_endpoint =
+        data.cross_origin_embedder_policy_report_only_reporting_endpoint();
   }
 
   return Status::kOk;
@@ -1626,11 +1639,26 @@
           ServiceWorkerRegistrationData_ServiceWorkerUpdateViaCacheType>(
           registration.update_via_cache));
 
-  data.set_cross_origin_embedder_policy(
-      registration.cross_origin_embedder_policy ==
+  data.set_cross_origin_embedder_policy_value(
+      registration.cross_origin_embedder_policy.value ==
               network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp
           ? ServiceWorkerRegistrationData::REQUIRE_CORP
           : ServiceWorkerRegistrationData::NONE_OR_NOT_EXIST);
+  if (registration.cross_origin_embedder_policy.reporting_endpoint) {
+    data.set_cross_origin_embedder_policy_reporting_endpoint(
+        registration.cross_origin_embedder_policy.reporting_endpoint.value());
+  }
+  data.set_cross_origin_embedder_policy_report_only_value(
+      registration.cross_origin_embedder_policy.report_only_value ==
+              network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp
+          ? ServiceWorkerRegistrationData::REQUIRE_CORP
+          : ServiceWorkerRegistrationData::NONE_OR_NOT_EXIST);
+  if (registration.cross_origin_embedder_policy
+          .report_only_reporting_endpoint) {
+    data.set_cross_origin_embedder_policy_report_only_reporting_endpoint(
+        registration.cross_origin_embedder_policy.report_only_reporting_endpoint
+            .value());
+  }
 
   std::string value;
   bool success = data.SerializeToString(&value);
diff --git a/content/browser/service_worker/service_worker_database.proto b/content/browser/service_worker/service_worker_database.proto
index 92bb9f0..5fadeea4 100644
--- a/content/browser/service_worker/service_worker_database.proto
+++ b/content/browser/service_worker/service_worker_database.proto
@@ -77,8 +77,14 @@
   // serialized by Time::ToDeltaSinceWindowsEpoch().
   optional int64 script_response_time = 16;
 
-  optional CrossOriginEmbedderPolicyValue cross_origin_embedder_policy = 17
+  optional CrossOriginEmbedderPolicyValue cross_origin_embedder_policy_value =
+      17 [default = NONE_OR_NOT_EXIST];
+  optional string cross_origin_embedder_policy_reporting_endpoint = 18;
+  optional CrossOriginEmbedderPolicyValue
+      cross_origin_embedder_policy_report_only_value = 19
       [default = NONE_OR_NOT_EXIST];
+  optional string cross_origin_embedder_policy_report_only_reporting_endpoint =
+      20;
 }
 
 message ServiceWorkerResourceRecord {
diff --git a/content/browser/service_worker/service_worker_database_unittest.cc b/content/browser/service_worker/service_worker_database_unittest.cc
index f8c02038..8406253 100644
--- a/content/browser/service_worker/service_worker_database_unittest.cc
+++ b/content/browser/service_worker/service_worker_database_unittest.cc
@@ -95,6 +95,16 @@
   }
 }
 
+network::CrossOriginEmbedderPolicy CrossOriginEmbedderPolicyNone() {
+  return network::CrossOriginEmbedderPolicy();
+}
+
+network::CrossOriginEmbedderPolicy CrossOriginEmbedderPolicyRequireCorp() {
+  network::CrossOriginEmbedderPolicy out;
+  out.value = network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+  return out;
+}
+
 }  // namespace
 
 TEST(ServiceWorkerDatabaseTest, OpenDatabase) {
@@ -450,8 +460,7 @@
   data1.version_id = 1000;
   data1.resources_total_size_bytes = 100;
   data1.script_response_time = base::Time::FromJsTime(0);
-  data1.cross_origin_embedder_policy =
-      network::mojom::CrossOriginEmbedderPolicyValue::kNone;
+  data1.cross_origin_embedder_policy = CrossOriginEmbedderPolicyNone();
   std::vector<ResourceRecordPtr> resources1;
   resources1.push_back(CreateResource(1, data1.script, 100));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
@@ -474,8 +483,7 @@
   data2.version_id = 2000;
   data2.resources_total_size_bytes = 200;
   data2.script_response_time = base::Time::FromJsTime(42);
-  data2.cross_origin_embedder_policy =
-      network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+  data2.cross_origin_embedder_policy = CrossOriginEmbedderPolicyRequireCorp();
   std::vector<ResourceRecordPtr> resources2;
   resources2.push_back(CreateResource(2, data2.script, 200));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
@@ -498,8 +506,7 @@
   data3.version_id = 3000;
   data3.resources_total_size_bytes = 300;
   data3.script_response_time = base::Time::FromJsTime(420);
-  data3.cross_origin_embedder_policy =
-      network::mojom::CrossOriginEmbedderPolicyValue::kNone;
+  data3.cross_origin_embedder_policy = CrossOriginEmbedderPolicyNone();
   std::vector<ResourceRecordPtr> resources3;
   resources3.push_back(CreateResource(3, data3.script, 300));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
@@ -513,8 +520,7 @@
   data4.version_id = 4000;
   data4.resources_total_size_bytes = 400;
   data4.script_response_time = base::Time::FromJsTime(4200);
-  data4.cross_origin_embedder_policy =
-      network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+  data4.cross_origin_embedder_policy = CrossOriginEmbedderPolicyRequireCorp();
   std::vector<ResourceRecordPtr> resources4;
   resources4.push_back(CreateResource(4, data4.script, 400));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
@@ -559,8 +565,7 @@
   data1.script = URL(origin1, "/script1.js");
   data1.version_id = 1000;
   data1.resources_total_size_bytes = 100;
-  data1.cross_origin_embedder_policy =
-      network::mojom::CrossOriginEmbedderPolicyValue::kNone;
+  data1.cross_origin_embedder_policy = CrossOriginEmbedderPolicyNone();
   std::vector<ResourceRecordPtr> resources1;
   resources1.push_back(CreateResource(1, data1.script, 100));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
@@ -574,8 +579,7 @@
   data2.version_id = 2000;
   data2.resources_total_size_bytes = 200;
   data2.update_via_cache = blink::mojom::ServiceWorkerUpdateViaCache::kNone;
-  data2.cross_origin_embedder_policy =
-      network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+  data2.cross_origin_embedder_policy = CrossOriginEmbedderPolicyRequireCorp();
   std::vector<ResourceRecordPtr> resources2;
   resources2.push_back(CreateResource(2, data2.script, 200));
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
@@ -2171,6 +2175,70 @@
   EXPECT_EQ(expect, registration->used_features);
 }
 
+// Check that every field of CrossOriginEmbedderPolicy can be properly
+// serialized and deserialized.
+TEST(ServiceWorkerDatabaseTest, CrossOriginEmbedderPolicyStoreRestore) {
+  auto store_and_restore = [](network::CrossOriginEmbedderPolicy policy) {
+    // Build the minimal RegistrationData with the given |policy|.
+    GURL origin("https://example.com");
+    RegistrationData data;
+    data.registration_id = 123;
+    data.scope = URL(origin, "/foo");
+    data.script = URL(origin, "/script.js");
+    data.version_id = 456;
+    data.resources_total_size_bytes = 100;
+    data.cross_origin_embedder_policy = policy;
+    std::vector<ResourceRecordPtr> resources;
+    resources.push_back(CreateResource(1, data.script, 100));
+
+    // Store.
+    std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
+    ServiceWorkerDatabase::DeletedVersion deleted_version;
+    ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
+              database->WriteRegistration(data, resources, &deleted_version));
+
+    // Restore.
+    std::vector<storage::mojom::ServiceWorkerRegistrationDataPtr> registrations;
+    std::vector<std::vector<ResourceRecordPtr>> resources_list;
+    EXPECT_EQ(ServiceWorkerDatabase::Status::kOk,
+              database->GetRegistrationsForOrigin(origin, &registrations,
+                                                  &resources_list));
+
+    // The data must not have been altered.
+    VerifyRegistrationData(data, *registrations[0]);
+  };
+
+  {
+    network::CrossOriginEmbedderPolicy policy;
+    policy.value = network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+    store_and_restore(policy);
+    policy.value = network::mojom::CrossOriginEmbedderPolicyValue::kNone;
+    store_and_restore(policy);
+  }
+
+  {
+    network::CrossOriginEmbedderPolicy policy;
+    policy.reporting_endpoint = "foo";
+    store_and_restore(policy);
+  }
+
+  {
+    network::CrossOriginEmbedderPolicy policy;
+    policy.report_only_value =
+        network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+    store_and_restore(policy);
+    policy.report_only_value =
+        network::mojom::CrossOriginEmbedderPolicyValue::kNone;
+    store_and_restore(policy);
+  }
+
+  {
+    network::CrossOriginEmbedderPolicy policy;
+    policy.report_only_reporting_endpoint = "bar";
+    store_and_restore(policy);
+  }
+}
+
 TEST(ServiceWorkerDatabaseTest, NoCrossOriginEmbedderPolicyValue) {
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
 
@@ -2198,7 +2266,7 @@
   ASSERT_EQ(ServiceWorkerDatabase::Status::kOk,
             database->ParseRegistrationData(value, &registration));
   EXPECT_EQ(network::mojom::CrossOriginEmbedderPolicyValue::kNone,
-            registration->cross_origin_embedder_policy);
+            registration->cross_origin_embedder_policy.value);
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index 67bd23ca..ffc4832b 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -149,6 +149,16 @@
   EmbeddedWorkerStatus expected_running_status_;
 };
 
+network::CrossOriginEmbedderPolicy CrossOriginEmbedderPolicyNone() {
+  return network::CrossOriginEmbedderPolicy();
+}
+
+network::CrossOriginEmbedderPolicy CrossOriginEmbedderPolicyRequireCorp() {
+  network::CrossOriginEmbedderPolicy out;
+  out.value = network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+  return out;
+}
+
 }  // namespace
 
 class ServiceWorkerJobTest : public testing::Test {
@@ -2055,7 +2065,7 @@
   scoped_refptr<ServiceWorkerRegistration> registration =
       update_helper_->SetupInitialRegistration(kNewVersionOrigin);
   ASSERT_TRUE(registration.get());
-  EXPECT_EQ(network::mojom::CrossOriginEmbedderPolicyValue::kNone,
+  EXPECT_EQ(CrossOriginEmbedderPolicyNone(),
             registration->active_version()->cross_origin_embedder_policy());
 
   registration->AddListener(update_helper_);
@@ -2098,7 +2108,7 @@
     histogram_tester.ExpectBucketCount("ServiceWorker.UpdateCheck.UpdateFound",
                                        true, 1);
     ASSERT_NE(nullptr, registration->waiting_version());
-    EXPECT_EQ(network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp,
+    EXPECT_EQ(CrossOriginEmbedderPolicyRequireCorp(),
               registration->waiting_version()->cross_origin_embedder_policy());
   }
 
@@ -2120,7 +2130,7 @@
     histogram_tester.ExpectBucketCount("ServiceWorker.UpdateCheck.UpdateFound",
                                        true, 1);
     ASSERT_NE(nullptr, registration->waiting_version());
-    EXPECT_EQ(network::mojom::CrossOriginEmbedderPolicyValue::kNone,
+    EXPECT_EQ(CrossOriginEmbedderPolicyNone(),
               registration->waiting_version()->cross_origin_embedder_policy());
   }
 }
diff --git a/content/browser/service_worker/service_worker_new_script_loader.cc b/content/browser/service_worker/service_worker_new_script_loader.cc
index b18d146e..4add383 100644
--- a/content/browser/service_worker/service_worker_new_script_loader.cc
+++ b/content/browser/service_worker/service_worker_new_script_loader.cc
@@ -205,7 +205,7 @@
     }
 
     version_->set_cross_origin_embedder_policy(
-        response_head->cross_origin_embedder_policy.value);
+        response_head->cross_origin_embedder_policy);
 
     if (response_head->network_accessed)
       version_->embedded_worker()->OnNetworkAccessedForScriptLoad();
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.cc b/content/browser/service_worker/service_worker_single_script_update_checker.cc
index 3d1f4326..6633084 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.cc
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.cc
@@ -278,8 +278,7 @@
            network::URLLoaderCompletionStatus(net::ERR_INSECURE_RESPONSE));
       return;
     }
-    cross_origin_embedder_policy_ =
-        response_head->cross_origin_embedder_policy.value;
+    cross_origin_embedder_policy_ = response_head->cross_origin_embedder_policy;
   }
 
   network_loader_state_ =
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.h b/content/browser/service_worker/service_worker_single_script_update_checker.h
index 728de85..bdac3408 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.h
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.h
@@ -145,7 +145,7 @@
   void OnComplete(const network::URLLoaderCompletionStatus& status) override;
 
   bool network_accessed() const { return network_accessed_; }
-  network::mojom::CrossOriginEmbedderPolicyValue cross_origin_embedder_policy()
+  const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy()
       const {
     return cross_origin_embedder_policy_;
   }
@@ -188,8 +188,7 @@
   const blink::mojom::ServiceWorkerUpdateViaCache update_via_cache_;
   const base::TimeDelta time_since_last_check_;
   bool network_accessed_ = false;
-  network::mojom::CrossOriginEmbedderPolicyValue cross_origin_embedder_policy_ =
-      network::mojom::CrossOriginEmbedderPolicyValue::kNone;
+  network::CrossOriginEmbedderPolicy cross_origin_embedder_policy_;
 
   std::unique_ptr<
       ServiceWorkerUpdatedScriptLoader::ThrottlingURLLoaderCoreWrapper>
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc
index 235eaf4c..457579e 100644
--- a/content/browser/service_worker/service_worker_storage_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -742,8 +742,10 @@
   live_version->script_cache_map()->SetResources(resources);
   live_version->set_used_features(
       std::set<blink::mojom::WebFeature>(used_features));
-  live_version->set_cross_origin_embedder_policy(
-      network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp);
+  network::CrossOriginEmbedderPolicy coep_require_corp;
+  coep_require_corp.value =
+      network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+  live_version->set_cross_origin_embedder_policy(coep_require_corp);
   live_registration->SetWaitingVersion(live_version);
   live_registration->set_last_update_check(kYesterday);
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
@@ -761,7 +763,7 @@
             found_registration->waiting_version()->used_features());
   EXPECT_EQ(
       found_registration->waiting_version()->cross_origin_embedder_policy(),
-      network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp);
+      coep_require_corp);
   found_registration = nullptr;
 
   // But FindRegistrationForScope is always async.
diff --git a/content/browser/service_worker/service_worker_test_utils.cc b/content/browser/service_worker/service_worker_test_utils.cc
index eba1a01..4057e943 100644
--- a/content/browser/service_worker/service_worker_test_utils.cc
+++ b/content/browser/service_worker/service_worker_test_utils.cc
@@ -774,7 +774,7 @@
        ServiceWorkerSingleScriptUpdateChecker::Result::kDifferent)
           ? script_url
           : GURL(),
-      network::mojom::CrossOriginEmbedderPolicyValue::kNone);
+      network::CrossOriginEmbedderPolicy());
 }
 
 void ServiceWorkerUpdateCheckTestUtils::
diff --git a/content/browser/service_worker/service_worker_update_checker.h b/content/browser/service_worker/service_worker_update_checker.h
index 21a2b5f..f356ca6d 100644
--- a/content/browser/service_worker/service_worker_update_checker.h
+++ b/content/browser/service_worker/service_worker_update_checker.h
@@ -111,8 +111,7 @@
 
   const GURL& updated_script_url() const { return updated_script_url_; }
   bool network_accessed() const { return network_accessed_; }
-  network::mojom::CrossOriginEmbedderPolicyValue cross_origin_embedder_policy()
-      const {
+  network::CrossOriginEmbedderPolicy cross_origin_embedder_policy() const {
     return cross_origin_embedder_policy_;
   }
 
@@ -159,9 +158,8 @@
   // True if any at least one of the scripts is fetched by network.
   bool network_accessed_ = false;
 
-  // The value of Cross-Origin-Embedder-Policy header for the updated main
-  // script.
-  network::mojom::CrossOriginEmbedderPolicyValue cross_origin_embedder_policy_;
+  // The Cross-Origin-Embedder-Policy header for the updated main script.
+  network::CrossOriginEmbedderPolicy cross_origin_embedder_policy_;
 
   // |context_| outlives |this| because it owns |this| through
   // ServiceWorkerJobCoordinator and ServiceWorkerRegisterJob.
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 07fe7974..5fe3884 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -2238,8 +2238,7 @@
     std::map<GURL, ServiceWorkerUpdateChecker::ComparedScriptInfo>
         compared_script_info_map,
     const GURL& updated_script_url,
-    network::mojom::CrossOriginEmbedderPolicyValue
-        cross_origin_embedder_policy) {
+    network::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
   compared_script_info_map_ = std::move(compared_script_info_map);
   updated_script_url_ = updated_script_url;
   cross_origin_embedder_policy_ = cross_origin_embedder_policy;
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index fc75325..0a37e0b 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -44,7 +44,7 @@
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "services/network/public/mojom/cross_origin_embedder_policy.mojom.h"
+#include "services/network/public/cpp/cross_origin_embedder_policy.h"
 #include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom.h"
@@ -512,12 +512,10 @@
   }
 
   void set_cross_origin_embedder_policy(
-      network::mojom::CrossOriginEmbedderPolicyValue
-          cross_origin_embedder_policy) {
+      network::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
     cross_origin_embedder_policy_ = cross_origin_embedder_policy;
   }
-  network::mojom::CrossOriginEmbedderPolicyValue cross_origin_embedder_policy()
-      const {
+  network::CrossOriginEmbedderPolicy cross_origin_embedder_policy() const {
     return cross_origin_embedder_policy_;
   }
 
@@ -554,8 +552,7 @@
       std::map<GURL, ServiceWorkerUpdateChecker::ComparedScriptInfo>
           compared_script_info_map,
       const GURL& updated_script_url,
-      network::mojom::CrossOriginEmbedderPolicyValue
-          cross_origin_embedder_policy);
+      network::CrossOriginEmbedderPolicy cross_origin_embedder_policy);
   const std::map<GURL, ServiceWorkerUpdateChecker::ComparedScriptInfo>&
   compared_script_info_map() const;
   ServiceWorkerUpdateChecker::ComparedScriptInfo TakeComparedScriptInfo(
@@ -892,8 +889,7 @@
   // Cross-Origin-Embedder-Policy for the service worker script. This persists
   // in the disk. kNone is set if this is a brand-new service worker whose main
   // script is not loaded yet.
-  network::mojom::CrossOriginEmbedderPolicyValue cross_origin_embedder_policy_ =
-      network::mojom::CrossOriginEmbedderPolicyValue::kNone;
+  network::CrossOriginEmbedderPolicy cross_origin_embedder_policy_;
 
   Status status_ = NEW;
   std::unique_ptr<EmbeddedWorkerInstance> embedded_worker_;
diff --git a/content/browser/service_worker/service_worker_version_browsertest.cc b/content/browser/service_worker/service_worker_version_browsertest.cc
index 13ee9250..787149e 100644
--- a/content/browser/service_worker/service_worker_version_browsertest.cc
+++ b/content/browser/service_worker/service_worker_version_browsertest.cc
@@ -281,6 +281,16 @@
   return http_response;
 }
 
+network::CrossOriginEmbedderPolicy CrossOriginEmbedderPolicyNone() {
+  return network::CrossOriginEmbedderPolicy();
+}
+
+network::CrossOriginEmbedderPolicy CrossOriginEmbedderPolicyRequireCorp() {
+  network::CrossOriginEmbedderPolicy out;
+  out.value = network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+  return out;
+}
+
 }  // namespace
 
 class ConsoleListener : public EmbeddedWorkerInstance::Listener {
@@ -1638,15 +1648,15 @@
   RunOnCoreThread(base::BindOnce(
       &ServiceWorkerVersionBrowserTest::SetUpRegistrationOnCoreThread,
       base::Unretained(this), "/service_worker/generated"));
-  EXPECT_EQ(network::mojom::CrossOriginEmbedderPolicyValue::kNone,
+  EXPECT_EQ(CrossOriginEmbedderPolicyNone(),
             version_->cross_origin_embedder_policy());
 
   // Once it's started, the worker script is read from the network and the COEP
   // value is set to the version.
   StartWorker(blink::ServiceWorkerStatusCode::kOk);
   EXPECT_EQ(IsCrossOriginIsolationEnabled()
-                ? network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp
-                : network::mojom::CrossOriginEmbedderPolicyValue::kNone,
+                ? CrossOriginEmbedderPolicyRequireCorp()
+                : CrossOriginEmbedderPolicyNone(),
             version_->cross_origin_embedder_policy());
 }
 
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index e4196bc..7d0aa842 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -558,11 +558,6 @@
                     std::string /* name */,
                     std::string /* unique_name */)
 
-// Update a proxy's replicated enforcement of insecure request policy.
-// Used when the frame's policy is changed in another process.
-IPC_MESSAGE_ROUTED1(FrameMsg_EnforceInsecureRequestPolicy,
-                    blink::mojom::InsecureRequestPolicy)
-
 // Send to the RenderFrame to set text tracks state and style settings.
 // Sent for top-level frames.
 IPC_MESSAGE_ROUTED1(FrameMsg_SetTextTrackSettings,
diff --git a/content/public/test/fake_remote_frame.cc b/content/public/test/fake_remote_frame.cc
index 3a27310..0e51675 100644
--- a/content/public/test/fake_remote_frame.cc
+++ b/content/public/test/fake_remote_frame.cc
@@ -32,6 +32,9 @@
 void FakeRemoteFrame::SetFrameOwnerProperties(
     blink::mojom::FrameOwnerPropertiesPtr properties) {}
 
+void FakeRemoteFrame::EnforceInsecureRequestPolicy(
+    blink::mojom::InsecureRequestPolicy policy) {}
+
 void FakeRemoteFrame::SetReplicatedOrigin(
     const url::Origin& origin,
     bool is_potentially_trustworthy_unique_origin) {}
diff --git a/content/public/test/fake_remote_frame.h b/content/public/test/fake_remote_frame.h
index 72ed0dc..b2525b6d 100644
--- a/content/public/test/fake_remote_frame.h
+++ b/content/public/test/fake_remote_frame.h
@@ -13,6 +13,7 @@
 #include "third_party/blink/public/mojom/frame/intrinsic_sizing_info.mojom.h"
 #include "third_party/blink/public/mojom/frame/user_activation_update_types.mojom.h"
 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom.h"
+#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom.h"
 #include "ui/events/types/scroll_types.h"
 
 namespace base {
@@ -45,6 +46,8 @@
   void EnforceInsecureNavigationsSet(const std::vector<uint32_t>& set) override;
   void SetFrameOwnerProperties(
       blink::mojom::FrameOwnerPropertiesPtr properties) override;
+  void EnforceInsecureRequestPolicy(
+      blink::mojom::InsecureRequestPolicy policy) override;
   void SetReplicatedOrigin(
       const url::Origin& origin,
       bool is_potentially_trustworthy_unique_origin) override;
diff --git a/content/renderer/input/render_widget_input_handler.cc b/content/renderer/input/render_widget_input_handler.cc
index 7db490c..4390820 100644
--- a/content/renderer/input/render_widget_input_handler.cc
+++ b/content/renderer/input/render_widget_input_handler.cc
@@ -445,7 +445,7 @@
     suppress_next_char_events_ = false;
     if (processed == WebInputEventResult::kNotHandled &&
         widget_->GetWebWidget()) {
-      if (!widget_->GetWebWidget()->IsPepperWidget() &&
+      if (delegate_->SupportsBufferedTouchEvents() &&
           WebInputEvent::IsTouchEventType(input_event.GetType()))
         processed = HandleTouchEvent(coalesced_event);
       else
diff --git a/content/renderer/input/render_widget_input_handler_delegate.h b/content/renderer/input/render_widget_input_handler_delegate.h
index 8d3e7d4..4491717c 100644
--- a/content/renderer/input/render_widget_input_handler_delegate.h
+++ b/content/renderer/input/render_widget_input_handler_delegate.h
@@ -70,8 +70,11 @@
   // won't be sent to WebKit or trigger DidHandleMouseEvent().
   virtual bool WillHandleMouseEvent(const blink::WebMouseEvent& event) = 0;
 
+  // Whether buffered touch events should be supported or not.
+  virtual bool SupportsBufferedTouchEvents() = 0;
+
  protected:
-  virtual ~RenderWidgetInputHandlerDelegate() {}
+  virtual ~RenderWidgetInputHandlerDelegate() = default;
 };
 
 }  // namespace content
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 60888e8..86914f7 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -374,8 +374,6 @@
   IPC_BEGIN_MESSAGE_MAP(RenderFrameProxy, msg)
     IPC_MESSAGE_HANDLER(FrameMsg_UpdateOpener, OnUpdateOpener)
     IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateName, OnDidUpdateName)
-    IPC_MESSAGE_HANDLER(FrameMsg_EnforceInsecureRequestPolicy,
-                        OnEnforceInsecureRequestPolicy)
     IPC_MESSAGE_HANDLER(FrameMsg_TransferUserActivationFrom,
                         OnTransferUserActivationFrom)
     IPC_MESSAGE_HANDLER(UnfreezableFrameMsg_DeleteProxy, OnDeleteProxy)
@@ -428,11 +426,6 @@
   unique_name_ = unique_name;
 }
 
-void RenderFrameProxy::OnEnforceInsecureRequestPolicy(
-    blink::mojom::InsecureRequestPolicy policy) {
-  web_frame_->SetReplicatedInsecureRequestPolicy(policy);
-}
-
 void RenderFrameProxy::OnTransferUserActivationFrom(int32_t source_routing_id) {
   RenderFrameProxy* source_proxy =
       RenderFrameProxy::FromRoutingID(source_routing_id);
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 5e9155c..6476397 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1543,6 +1543,11 @@
   return mouse_lock_dispatcher()->WillHandleMouseEvent(event);
 }
 
+bool RenderWidget::SupportsBufferedTouchEvents() {
+  // Buffered touch events aren't supported for pepper.
+  return !pepper_fullscreen_;
+}
+
 void RenderWidget::ResizeWebWidget() {
   // In auto resize mode, blink controls sizes and RenderWidget should not be
   // passing values back in.
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index a5e2f92..7772dfa4 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -351,6 +351,7 @@
   void ClearTextInputState() override;
   bool WillHandleGestureEvent(const blink::WebGestureEvent& event) override;
   bool WillHandleMouseEvent(const blink::WebMouseEvent& event) override;
+  bool SupportsBufferedTouchEvents() override;
 
   // RenderWidgetScreenMetricsEmulatorDelegate
   void SetScreenMetricsEmulationParameters(
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index cbdb2df..47e8ce0e 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -41,7 +41,6 @@
 using blink::WebString;
 using blink::WebTextInputType;
 using blink::WebVector;
-using blink::WebWidget;
 
 namespace content {
 
@@ -126,142 +125,34 @@
   widget_->Send(new WidgetHostMsg_UnlockMouse(widget_->routing_id()));
 }
 
-// WebWidget that simply wraps the pepper plugin.
-// TODO(piman): figure out IME and implement setComposition and friends if
-// necessary.
-class PepperWidget : public WebWidget {
+}  // anonymous namespace
+
+// We place the WebExternalWidgetClient interface on a separate class because
+// RenderWidget implements blink::WebWidgetClient, which is not used for
+// WebExternalWidgets, but may have similar method definitions as this
+// interface.
+class PepperExternalWidgetClient : public blink::WebExternalWidgetClient {
  public:
-  explicit PepperWidget(RenderWidgetFullscreenPepper* widget,
-                        const blink::WebURL& local_main_frame_url)
-      : widget_(widget), local_main_frame_url_(local_main_frame_url) {}
+  explicit PepperExternalWidgetClient(RenderWidgetFullscreenPepper* widget)
+      : widget_(widget) {}
+  ~PepperExternalWidgetClient() override = default;
 
-  virtual ~PepperWidget() = default;
-
-  // WebWidget API
-  void SetCompositorHosts(cc::LayerTreeHost*, cc::AnimationHost*) override {
-    // These pointers are not stored as they aren't needed.
+  // blink::WebExternalWidgetClient overrides:
+  blink::WebInputEventResult HandleInputEvent(
+      const blink::WebCoalescedInputEvent& event) override {
+    return widget_->ProcessInputEvent(event);
   }
 
-  void Close() override { delete this; }
-
-  WebSize Size() override { return size_; }
-
-  bool IsPepperWidget() const override { return true; }
-
-  void Resize(const WebSize& size) override {
-    if (!widget_->plugin() || size_ == size)
-      return;
-
-    size_ = size;
-    WebRect plugin_rect(0, 0, size_.width, size_.height);
-    widget_->plugin()->ViewChanged(plugin_rect, plugin_rect, plugin_rect);
+  blink::WebInputEventResult DispatchBufferedTouchEvents() override {
+    return WebInputEventResult::kNotHandled;
   }
 
-  void ThemeChanged() override { NOTIMPLEMENTED(); }
-
-  blink::WebHitTestResult HitTestResultAt(const gfx::Point&) override {
-    NOTIMPLEMENTED();
-    return {};
-  }
-
-  WebInputEventResult HandleInputEvent(
-      const WebCoalescedInputEvent& coalesced_event) override {
-    if (!widget_->plugin())
-      return WebInputEventResult::kNotHandled;
-
-    const WebInputEvent& event = coalesced_event.Event();
-
-    // This cursor info is ignored, we always set the cursor directly from
-    // RenderWidgetFullscreenPepper::DidChangeCursor.
-    WebCursorInfo cursor;
-
-    // Pepper plugins do not accept gesture events. So do not send the gesture
-    // events directly to the plugin. Instead, try to convert them to equivalent
-    // mouse events, and then send to the plugin.
-    if (WebInputEvent::IsGestureEventType(event.GetType())) {
-      bool result = false;
-      const WebGestureEvent* gesture_event =
-          static_cast<const WebGestureEvent*>(&event);
-      switch (event.GetType()) {
-        case WebInputEvent::kGestureTap: {
-          WebMouseEvent mouse(WebInputEvent::kMouseMove,
-                              gesture_event->GetModifiers(),
-                              gesture_event->TimeStamp());
-          mouse.SetPositionInWidget(gesture_event->PositionInWidget());
-          mouse.SetPositionInScreen(gesture_event->PositionInScreen());
-          mouse.movement_x = 0;
-          mouse.movement_y = 0;
-          result |= widget_->plugin()->HandleInputEvent(mouse, &cursor);
-
-          mouse.SetType(WebInputEvent::kMouseDown);
-          mouse.button = WebMouseEvent::Button::kLeft;
-          mouse.click_count = gesture_event->data.tap.tap_count;
-          result |= widget_->plugin()->HandleInputEvent(mouse, &cursor);
-
-          mouse.SetType(WebInputEvent::kMouseUp);
-          result |= widget_->plugin()->HandleInputEvent(mouse, &cursor);
-          break;
-        }
-
-        default: {
-          WebMouseEvent mouse = WebMouseEventFromGestureEvent(*gesture_event);
-          if (mouse.GetType() != WebInputEvent::kUndefined)
-            result |= widget_->plugin()->HandleInputEvent(mouse, &cursor);
-          break;
-        }
-      }
-      return result ? WebInputEventResult::kHandledApplication
-                    : WebInputEventResult::kNotHandled;
-    }
-
-    bool result = widget_->plugin()->HandleInputEvent(event, &cursor);
-
-    // For normal web pages, WebViewImpl does input event translations and
-    // generates context menu events. Since we don't have a WebView, we need to
-    // do the necessary translation ourselves.
-    if (WebInputEvent::IsMouseEventType(event.GetType())) {
-      const WebMouseEvent& mouse_event =
-          reinterpret_cast<const WebMouseEvent&>(event);
-      bool send_context_menu_event = false;
-      // On Mac/Linux, we handle it on mouse down.
-      // On Windows, we handle it on mouse up.
-#if defined(OS_WIN)
-      send_context_menu_event =
-          mouse_event.GetType() == WebInputEvent::kMouseUp &&
-          mouse_event.button == WebMouseEvent::Button::kRight;
-#elif defined(OS_MACOSX)
-      send_context_menu_event =
-          mouse_event.GetType() == WebInputEvent::kMouseDown &&
-          (mouse_event.button == WebMouseEvent::Button::kRight ||
-           (mouse_event.button == WebMouseEvent::Button::kLeft &&
-            mouse_event.GetModifiers() & WebMouseEvent::kControlKey));
-#else
-      send_context_menu_event =
-          mouse_event.GetType() == WebInputEvent::kMouseDown &&
-          mouse_event.button == WebMouseEvent::Button::kRight;
-#endif
-      if (send_context_menu_event) {
-        WebMouseEvent context_menu_event(mouse_event);
-        context_menu_event.SetType(WebInputEvent::kContextMenu);
-        widget_->plugin()->HandleInputEvent(context_menu_event, &cursor);
-      }
-    }
-    return result ? WebInputEventResult::kHandledApplication
-                  : WebInputEventResult::kNotHandled;
-  }
-
-  blink::WebURL GetURLForDebugTrace() override { return local_main_frame_url_; }
+  void DidResize(const gfx::Size& size) override { widget_->DidResize(size); }
 
  private:
   RenderWidgetFullscreenPepper* widget_;
-  WebSize size_;
-  blink::WebURL local_main_frame_url_;
-
-  DISALLOW_COPY_AND_ASSIGN(PepperWidget);
 };
 
-}  // anonymous namespace
-
 // static
 RenderWidgetFullscreenPepper* RenderWidgetFullscreenPepper::Create(
     int32_t routing_id,
@@ -273,19 +164,22 @@
     mojo::PendingReceiver<mojom::Widget> widget_receiver) {
   DCHECK_NE(MSG_ROUTING_NONE, routing_id);
   DCHECK(show_callback);
-  RenderWidgetFullscreenPepper* widget = new RenderWidgetFullscreenPepper(
-      routing_id, compositor_deps, plugin, std::move(widget_receiver));
-  widget->InitForPepperFullscreen(
-      std::move(show_callback), new PepperWidget(widget, local_main_frame_url),
-      screen_info);
-  return widget;
+  RenderWidgetFullscreenPepper* render_widget =
+      new RenderWidgetFullscreenPepper(routing_id, compositor_deps, plugin,
+                                       std::move(widget_receiver),
+                                       local_main_frame_url);
+  render_widget->InitForPepperFullscreen(std::move(show_callback),
+                                         render_widget->blink_widget_.get(),
+                                         screen_info);
+  return render_widget;
 }
 
 RenderWidgetFullscreenPepper::RenderWidgetFullscreenPepper(
     int32_t routing_id,
     CompositorDependencies* compositor_deps,
     PepperPluginInstanceImpl* plugin,
-    mojo::PendingReceiver<mojom::Widget> widget_receiver)
+    mojo::PendingReceiver<mojom::Widget> widget_receiver,
+    blink::WebURL main_frame_url)
     : RenderWidget(routing_id,
                    compositor_deps,
                    /*display_mode=*/blink::mojom::DisplayMode::kUndefined,
@@ -294,11 +188,14 @@
                    std::move(widget_receiver)),
       plugin_(plugin),
       mouse_lock_dispatcher_(
-          std::make_unique<FullscreenMouseLockDispatcher>(this)) {}
-
-RenderWidgetFullscreenPepper::~RenderWidgetFullscreenPepper() {
+          std::make_unique<FullscreenMouseLockDispatcher>(this)),
+      widget_client_(std::make_unique<PepperExternalWidgetClient>(this)) {
+  blink_widget_ =
+      blink::WebExternalWidget::Create(widget_client_.get(), main_frame_url);
 }
 
+RenderWidgetFullscreenPepper::~RenderWidgetFullscreenPepper() = default;
+
 void RenderWidgetFullscreenPepper::Destroy() {
   // The plugin instance is going away reset any lock target that is set
   // on the dispatcher since this object can still live and receive IPC
@@ -327,13 +224,13 @@
 void RenderWidgetFullscreenPepper::SetLayer(scoped_refptr<cc::Layer> layer) {
   layer_ = layer.get();
   if (!layer_) {
-    layer_tree_host()->SetRootLayer(nullptr);
+    blink_widget_->SetRootLayer(nullptr);
     return;
   }
   UpdateLayerBounds();
   layer_->SetIsDrawable(true);
   layer_->SetHitTestable(true);
-  layer_tree_host()->SetNonBlinkManagedRootLayer(std::move(layer));
+  blink_widget_->SetRootLayer(std::move(layer));
 }
 
 bool RenderWidgetFullscreenPepper::OnMessageReceived(const IPC::Message& msg) {
@@ -388,4 +285,97 @@
   layer_->SetBounds(layer_size);
 }
 
+WebInputEventResult RenderWidgetFullscreenPepper::ProcessInputEvent(
+    const WebCoalescedInputEvent& coalesced_event) {
+  if (!plugin())
+    return WebInputEventResult::kNotHandled;
+
+  const WebInputEvent& event = coalesced_event.Event();
+
+  // This cursor info is ignored, we always set the cursor directly from
+  // RenderWidgetFullscreenPepper::DidChangeCursor.
+  blink::WebCursorInfo cursor;
+
+  // Pepper plugins do not accept gesture events. So do not send the gesture
+  // events directly to the plugin. Instead, try to convert them to equivalent
+  // mouse events, and then send to the plugin.
+  if (blink::WebInputEvent::IsGestureEventType(event.GetType())) {
+    bool result = false;
+    const WebGestureEvent* gesture_event =
+        static_cast<const WebGestureEvent*>(&event);
+    switch (event.GetType()) {
+      case WebInputEvent::kGestureTap: {
+        WebMouseEvent mouse(WebInputEvent::kMouseMove,
+                            gesture_event->GetModifiers(),
+                            gesture_event->TimeStamp());
+        mouse.SetPositionInWidget(gesture_event->PositionInWidget());
+        mouse.SetPositionInScreen(gesture_event->PositionInScreen());
+        mouse.movement_x = 0;
+        mouse.movement_y = 0;
+        result |= plugin()->HandleInputEvent(mouse, &cursor);
+
+        mouse.SetType(WebInputEvent::kMouseDown);
+        mouse.button = WebMouseEvent::Button::kLeft;
+        mouse.click_count = gesture_event->data.tap.tap_count;
+        result |= plugin()->HandleInputEvent(mouse, &cursor);
+
+        mouse.SetType(WebInputEvent::kMouseUp);
+        result |= plugin()->HandleInputEvent(mouse, &cursor);
+        break;
+      }
+
+      default: {
+        WebMouseEvent mouse = WebMouseEventFromGestureEvent(*gesture_event);
+        if (mouse.GetType() != WebInputEvent::kUndefined)
+          result |= plugin()->HandleInputEvent(mouse, &cursor);
+        break;
+      }
+    }
+    return result ? WebInputEventResult::kHandledApplication
+                  : WebInputEventResult::kNotHandled;
+  }
+
+  bool result = plugin()->HandleInputEvent(event, &cursor);
+
+  // For normal web pages, WebViewImpl does input event translations and
+  // generates context menu events. Since we don't have a WebView, we need to
+  // do the necessary translation ourselves.
+  if (WebInputEvent::IsMouseEventType(event.GetType())) {
+    const WebMouseEvent& mouse_event =
+        reinterpret_cast<const WebMouseEvent&>(event);
+    bool send_context_menu_event = false;
+    // On Mac/Linux, we handle it on mouse down.
+    // On Windows, we handle it on mouse up.
+#if defined(OS_WIN)
+    send_context_menu_event =
+        mouse_event.GetType() == WebInputEvent::kMouseUp &&
+        mouse_event.button == WebMouseEvent::Button::kRight;
+#elif defined(OS_MACOSX)
+    send_context_menu_event =
+        mouse_event.GetType() == WebInputEvent::kMouseDown &&
+        (mouse_event.button == WebMouseEvent::Button::kRight ||
+         (mouse_event.button == WebMouseEvent::Button::kLeft &&
+          mouse_event.GetModifiers() & WebMouseEvent::kControlKey));
+#else
+    send_context_menu_event =
+        mouse_event.GetType() == WebInputEvent::kMouseDown &&
+        mouse_event.button == WebMouseEvent::Button::kRight;
+#endif
+    if (send_context_menu_event) {
+      WebMouseEvent context_menu_event(mouse_event);
+      context_menu_event.SetType(WebInputEvent::kContextMenu);
+      plugin()->HandleInputEvent(context_menu_event, &cursor);
+    }
+  }
+  return result ? WebInputEventResult::kHandledApplication
+                : WebInputEventResult::kNotHandled;
+}
+
+void RenderWidgetFullscreenPepper::DidResize(const gfx::Size& size) {
+  if (!plugin())
+    return;
+  gfx::Rect plugin_rect(size);
+  plugin()->ViewChanged(plugin_rect, plugin_rect, plugin_rect);
+}
+
 }  // namespace content
diff --git a/content/renderer/render_widget_fullscreen_pepper.h b/content/renderer/render_widget_fullscreen_pepper.h
index 6bdea29..24350792 100644
--- a/content/renderer/render_widget_fullscreen_pepper.h
+++ b/content/renderer/render_widget_fullscreen_pepper.h
@@ -14,7 +14,8 @@
 #include "content/renderer/pepper/fullscreen_container.h"
 #include "content/renderer/render_widget.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "third_party/blink/public/web/web_widget.h"
+#include "third_party/blink/public/web/web_external_widget.h"
+#include "third_party/blink/public/web/web_external_widget_client.h"
 #include "url/gurl.h"
 
 namespace cc {
@@ -24,6 +25,7 @@
 namespace content {
 class CompositorDependencies;
 class PepperPluginInstanceImpl;
+class PepperExternalWidgetClient;
 
 // A RenderWidget that hosts a fullscreen pepper plugin. This provides a
 // FullscreenContainer that the plugin instance can callback into to e.g.
@@ -62,7 +64,8 @@
       int32_t routing_id,
       CompositorDependencies* compositor_deps,
       PepperPluginInstanceImpl* plugin,
-      mojo::PendingReceiver<mojom::Widget> widget_receiver);
+      mojo::PendingReceiver<mojom::Widget> widget_receiver,
+      blink::WebURL main_frame_url);
   ~RenderWidgetFullscreenPepper() override;
 
   // RenderWidget API.
@@ -71,7 +74,12 @@
   void AfterUpdateVisualProperties() override;
 
  private:
+  friend class PepperExternalWidgetClient;
+
   void UpdateLayerBounds();
+  void DidResize(const gfx::Size& size);
+  blink::WebInputEventResult ProcessInputEvent(
+      const blink::WebCoalescedInputEvent& event);
 
   // The plugin instance this widget wraps.
   PepperPluginInstanceImpl* plugin_;
@@ -79,6 +87,8 @@
   cc::Layer* layer_ = nullptr;
 
   std::unique_ptr<MouseLockDispatcher> mouse_lock_dispatcher_;
+  std::unique_ptr<PepperExternalWidgetClient> widget_client_;
+  std::unique_ptr<blink::WebExternalWidget> blink_widget_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetFullscreenPepper);
 };
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index 73dd792..6351e8e 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -41,7 +41,8 @@
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/web/web_device_emulation_params.h"
-#include "third_party/blink/public/web/web_widget.h"
+#include "third_party/blink/public/web/web_external_widget.h"
+#include "third_party/blink/public/web/web_external_widget_client.h"
 #include "ui/base/mojom/cursor_type.mojom-shared.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/blink/web_input_event_traits.h"
@@ -150,15 +151,12 @@
   DISALLOW_COPY_AND_ASSIGN(MockHandledEventCallback);
 };
 
-class MockWebWidget : public blink::WebWidget {
+class MockWebExternalWidgetClient : public blink::WebExternalWidgetClient {
  public:
-  // WebWidget implementation.
-  void SetCompositorHosts(cc::LayerTreeHost*, cc::AnimationHost*) override {}
-  blink::WebURL GetURLForDebugTrace() override { return {}; }
-  blink::WebHitTestResult HitTestResultAt(const gfx::Point&) override {
-    return {};
-  }
+  MockWebExternalWidgetClient() = default;
 
+  // WebExternalWidgetClient implementation.
+  MOCK_METHOD1(DidResize, void(const gfx::Size& size));
   MOCK_METHOD0(DispatchBufferedTouchEvents, blink::WebInputEventResult());
   MOCK_METHOD1(
       HandleInputEvent,
@@ -177,8 +175,10 @@
                      /*is_hidden=*/false,
                      /*never_composited=*/false,
                      mojo::NullReceiver()),
-        always_overscroll_(false) {
-    Initialize(base::NullCallback(), &mock_webwidget_, screen_info);
+        external_web_widget_(
+            blink::WebExternalWidget::Create(&mock_web_external_widget_client_,
+                                             blink::WebURL())) {
+    Initialize(base::NullCallback(), external_web_widget_.get(), screen_info);
 
     mock_input_handler_host_ = std::make_unique<MockWidgetInputHandlerHost>();
 
@@ -203,12 +203,18 @@
 
   IPC::TestSink* sink() { return &sink_; }
 
-  MockWebWidget* mock_webwidget() { return &mock_webwidget_; }
+  MockWebExternalWidgetClient* mock_web_external_widget_client() {
+    return &mock_web_external_widget_client_;
+  }
 
   MockWidgetInputHandlerHost* mock_input_handler_host() {
     return mock_input_handler_host_.get();
   }
 
+  blink::WebExternalWidget* external_web_widget() {
+    return external_web_widget_.get();
+  }
+
   const viz::LocalSurfaceIdAllocation& local_surface_id_allocation_from_parent()
       const {
     return local_surface_id_allocation_from_parent_;
@@ -240,8 +246,9 @@
 
  private:
   IPC::TestSink sink_;
-  bool always_overscroll_;
-  MockWebWidget mock_webwidget_;
+  bool always_overscroll_ = false;
+  MockWebExternalWidgetClient mock_web_external_widget_client_;
+  std::unique_ptr<blink::WebExternalWidget> external_web_widget_;
   std::unique_ptr<MockWidgetInputHandlerHost> mock_input_handler_host_;
   static int next_routing_id_;
 
@@ -298,7 +305,7 @@
   widget()->DidChangeCursor(cursor_info);
   EXPECT_EQ(widget()->sink()->message_count(), 0U);
 
-  EXPECT_CALL(*widget()->mock_webwidget(), HandleInputEvent(_))
+  EXPECT_CALL(*widget()->mock_web_external_widget_client(), HandleInputEvent(_))
       .WillOnce(::testing::Return(blink::WebInputEventResult::kNotHandled));
   widget()->SendInputEvent(SyntheticWebMouseEventBuilder::Build(
                                blink::WebInputEvent::Type::kMouseLeave),
@@ -314,7 +321,7 @@
 TEST_F(RenderWidgetUnittest, EventOverscroll) {
   widget()->set_always_overscroll(true);
 
-  EXPECT_CALL(*widget()->mock_webwidget(), HandleInputEvent(_))
+  EXPECT_CALL(*widget()->mock_web_external_widget_client(), HandleInputEvent(_))
       .WillRepeatedly(
           ::testing::Return(blink::WebInputEventResult::kNotHandled));
 
@@ -345,12 +352,13 @@
   touch.PressPoint(10, 10);
   touch.touch_start_or_first_touch_move = true;
 
-  EXPECT_CALL(*widget()->mock_webwidget(), HandleInputEvent(_))
+  EXPECT_CALL(*widget()->mock_web_external_widget_client(), HandleInputEvent(_))
       .Times(5)
       .WillRepeatedly(
           ::testing::Return(blink::WebInputEventResult::kNotHandled));
 
-  EXPECT_CALL(*widget()->mock_webwidget(), DispatchBufferedTouchEvents())
+  EXPECT_CALL(*widget()->mock_web_external_widget_client(),
+              DispatchBufferedTouchEvents())
       .Times(5)
       .WillRepeatedly(
           ::testing::Return(blink::WebInputEventResult::kNotHandled));
@@ -387,9 +395,10 @@
       EVENT_LISTENER_RESULT_HISTOGRAM,
       PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING, 2);
 
-  EXPECT_CALL(*widget()->mock_webwidget(), HandleInputEvent(_))
+  EXPECT_CALL(*widget()->mock_web_external_widget_client(), HandleInputEvent(_))
       .WillOnce(::testing::Return(blink::WebInputEventResult::kNotHandled));
-  EXPECT_CALL(*widget()->mock_webwidget(), DispatchBufferedTouchEvents())
+  EXPECT_CALL(*widget()->mock_web_external_widget_client(),
+              DispatchBufferedTouchEvents())
       .WillOnce(
           ::testing::Return(blink::WebInputEventResult::kHandledSuppressed));
   touch.dispatch_type = blink::WebInputEvent::DispatchType::kBlocking;
@@ -397,9 +406,10 @@
   histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM,
                                        PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED, 1);
 
-  EXPECT_CALL(*widget()->mock_webwidget(), HandleInputEvent(_))
+  EXPECT_CALL(*widget()->mock_web_external_widget_client(), HandleInputEvent(_))
       .WillOnce(::testing::Return(blink::WebInputEventResult::kNotHandled));
-  EXPECT_CALL(*widget()->mock_webwidget(), DispatchBufferedTouchEvents())
+  EXPECT_CALL(*widget()->mock_web_external_widget_client(),
+              DispatchBufferedTouchEvents())
       .WillOnce(
           ::testing::Return(blink::WebInputEventResult::kHandledApplication));
   touch.dispatch_type = blink::WebInputEvent::DispatchType::kBlocking;
diff --git a/content/shell/browser/web_test/web_test_devtools_bindings.cc b/content/shell/browser/web_test/web_test_devtools_bindings.cc
index 09869458..7ca556d 100644
--- a/content/shell/browser/web_test/web_test_devtools_bindings.cc
+++ b/content/shell/browser/web_test/web_test_devtools_bindings.cc
@@ -39,14 +39,9 @@
 
 class WebTestDevToolsBindings::SecondaryObserver : public WebContentsObserver {
  public:
-  SecondaryObserver(WebTestDevToolsBindings* bindings, bool is_startup_test)
+  explicit SecondaryObserver(WebTestDevToolsBindings* bindings)
       : WebContentsObserver(bindings->inspected_contents()),
-        bindings_(bindings) {
-    if (is_startup_test) {
-      bindings_->NavigateDevToolsFrontend();
-      bindings_ = nullptr;
-    }
-  }
+        bindings_(bindings) {}
 
   // WebContentsObserver implementation.
   void DocumentAvailableInMainFrame() override {
@@ -108,12 +103,6 @@
 }
 
 void WebTestDevToolsBindings::Attach() {
-  DCHECK(is_startup_test_);
-  ShellDevToolsBindings::Attach();
-  web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
-      base::UTF8ToUTF16("TestRunner._startupTestSetupFinished();\n//# "
-                        "sourceURL=layout_test_devtools_bindings.cc"),
-      base::NullCallback());
 }
 
 WebTestDevToolsBindings::WebTestDevToolsBindings(
@@ -122,12 +111,7 @@
     const GURL& frontend_url)
     : ShellDevToolsBindings(devtools_contents, inspected_contents, nullptr),
       frontend_url_(frontend_url) {
-  is_startup_test_ =
-      frontend_url.query().find("/startup/") != std::string::npos;
-  secondary_observer_ =
-      std::make_unique<SecondaryObserver>(this, is_startup_test_);
-  if (is_startup_test_)
-    return;
+  secondary_observer_ = std::make_unique<SecondaryObserver>(this);
   NavigationController::LoadURLParams params(GetInspectedPageURL(frontend_url));
   params.transition_type = ui::PageTransitionFromInt(
       ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
@@ -149,8 +133,6 @@
 }
 
 void WebTestDevToolsBindings::DocumentAvailableInMainFrame() {
-  if (is_startup_test_)
-    return;
   ShellDevToolsBindings::Attach();
 }
 
diff --git a/content/shell/browser/web_test/web_test_devtools_bindings.h b/content/shell/browser/web_test/web_test_devtools_bindings.h
index 65c8fc0..1e64234 100644
--- a/content/shell/browser/web_test/web_test_devtools_bindings.h
+++ b/content/shell/browser/web_test/web_test_devtools_bindings.h
@@ -35,7 +35,6 @@
 
   void NavigateDevToolsFrontend();
 
-  bool is_startup_test_ = false;
   GURL frontend_url_;
   std::unique_ptr<SecondaryObserver> secondary_observer_;
 
diff --git a/docs/asan.md b/docs/asan.md
index 293e9f82..e3fc5c5 100644
--- a/docs/asan.md
+++ b/docs/asan.md
@@ -120,7 +120,7 @@
 ... lots more stuff
 ```
 
-Congrats, you have a working ASan build! 🙌
+Congrats, you have a working ASan build! &#x1F64C;
 
 ## Run chrome under ASan
 
@@ -210,14 +210,15 @@
 To run stuff without Chromium testing script (ex. ContentShell.apk, or any third
 party apk or binary), device setup is needed:
 ```shell
-tools/android/asan/third_party/asan_device_setup.sh --lib
-third_party/llvm-build/Release+Asserts/lib/clang/*/lib/linux/libclang_rt.asan-arm-android.so
+tools/android/asan/third_party/asan_device_setup.sh \
+    --lib third_party/llvm-build/Release+Asserts/lib/clang/*/lib/linux/libclang_rt.asan-arm-android.so
 # wait a few seconds for the device to reload
 ```
 
 It only needs to be run once per device. It is safe to run it multiple times.
-When this is done, the device will run ASan apks as well as normal apks without
-any further setup.
+Examine the output to ensure that setup was successful (you may need to run
+`adb disable-verity` and restart the device first). When this is done, the
+device will run ASan apks as well as normal apks without any further setup.
 
 To run command-line tools (i.e. binaries), prefix them with `asanwrapper`:
 ```shell
diff --git a/docs/debugging_with_crash_keys.md b/docs/debugging_with_crash_keys.md
index 400f948..b45c998 100644
--- a/docs/debugging_with_crash_keys.md
+++ b/docs/debugging_with_crash_keys.md
@@ -32,6 +32,14 @@
 switches, active extension IDs, GPU vendor information, experiment/variations
 information, etc.
 
+## Redaction
+
+Beware that certain on certain platforms (e.g. Android Webview) we
+[sanitize the stack in the dump](https://cs.chromium.org/chromium/src/third_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized.h)
+and only crash keys on a
+[whitelist](https://cs.chromium.org/chromium/src/android_webview/common/crash_reporter/crash_keys.cc)
+will be captured.
+
 ## Getting Started with a Single Key-Value Pair
 
 Imagine you are investigating a crash, and you want to know the value of some
diff --git a/fuchsia/engine/context_provider_impl.cc b/fuchsia/engine/context_provider_impl.cc
index 381867a..373ece5 100644
--- a/fuchsia/engine/context_provider_impl.cc
+++ b/fuchsia/engine/context_provider_impl.cc
@@ -462,9 +462,6 @@
             .value_or(false);
     if (force_protected_video_buffers) {
       launch_command.AppendSwitch(switches::kForceProtectedVideoOutputBuffers);
-      // TODO(crbug.com/1019212): We observed flicker and buffer issues when
-      // using accelerated canvas with protected memory.
-      launch_command.AppendSwitch(switches::kDisableAccelerated2dCanvas);
     }
   }
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
index 6b3fceb..1573e77 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
@@ -384,22 +384,15 @@
                                 GLsizei* length,
                                 T* params,
                                 GLGetFunction get_call) {
-    // Create a scratch buffer to hold the result of the query
-    std::vector<T> scratch_params(bufsize);
-    get_call(pname, bufsize, length, scratch_params.data());
+    get_call(pname, bufsize, length, params);
 
     // Update the results of the query, if needed
-    error::Error error =
-        PatchGetNumericResults(pname, *length, scratch_params.data());
+    const error::Error error = PatchGetNumericResults(pname, *length, params);
     if (error != error::kNoError) {
       *length = 0;
       return error;
     }
 
-    // Copy into the destination
-    DCHECK(*length <= bufsize);
-    std::copy(scratch_params.data(), scratch_params.data() + *length, params);
-
     return error::kNoError;
   }
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 0d3a45d..bc1be8b 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -1680,11 +1680,8 @@
 
   CheckErrorCallbackState();
 
-  // Create a scratch buffer to hold the result of the query
-  std::vector<GLint> scratch_params(bufsize);
   api()->glGetFramebufferAttachmentParameterivRobustANGLEFn(
-      target, updated_attachment, pname, bufsize, length,
-      scratch_params.data());
+      target, updated_attachment, pname, bufsize, length, params);
 
   if (CheckErrorCallbackState()) {
     DCHECK(*length == 0);
@@ -1692,17 +1689,13 @@
   }
 
   // Update the results of the query, if needed
-  error::Error error = PatchGetFramebufferAttachmentParameter(
-      target, updated_attachment, pname, *length, scratch_params.data());
+  const error::Error error = PatchGetFramebufferAttachmentParameter(
+      target, updated_attachment, pname, *length, params);
   if (error != error::kNoError) {
     *length = 0;
     return error;
   }
 
-  // Copy into the destination
-  DCHECK(*length < bufsize);
-  std::copy(scratch_params.data(), scratch_params.data() + *length, params);
-
   return error::kNoError;
 }
 
diff --git a/gpu/command_buffer/service/shared_image_video.cc b/gpu/command_buffer/service/shared_image_video.cc
index 0240c02..2dbe03b 100644
--- a/gpu/command_buffer/service/shared_image_video.cc
+++ b/gpu/command_buffer/service/shared_image_video.cc
@@ -497,14 +497,24 @@
   }
 
   DCHECK(context_state->GrContextIsGL());
-  auto* texture = stream_texture_sii_->GetTexture();
-  DCHECK(texture);
+  auto* texture_base = stream_texture_sii_->GetTextureBase();
+  DCHECK(texture_base);
 
-  // In GL mode, create the SharedImageRepresentationGLTextureVideo
+  // In GL mode, create the SharedImageRepresentationGLTexture*Video
   // representation to use with SharedImageRepresentationVideoSkiaGL.
-  auto gl_representation =
-      std::make_unique<SharedImageRepresentationGLTextureVideo>(
-          manager, this, tracker, texture);
+  std::unique_ptr<gpu::SharedImageRepresentationGLTextureBase>
+      gl_representation;
+  if (texture_base->GetType() == gpu::TextureBase::Type::kValidated) {
+    gl_representation =
+        std::make_unique<SharedImageRepresentationGLTextureVideo>(
+            manager, this, tracker, gles2::Texture::CheckedCast(texture_base));
+  } else {
+    gl_representation =
+        std::make_unique<SharedImageRepresentationGLTexturePassthroughVideo>(
+            manager, this, tracker,
+            gles2::TexturePassthrough::CheckedCast(texture_base));
+  }
+
   return SharedImageRepresentationSkiaGL::Create(std::move(gl_representation),
                                                  std::move(context_state),
                                                  manager, this, tracker);
diff --git a/gpu/command_buffer/service/stream_texture_shared_image_interface.h b/gpu/command_buffer/service/stream_texture_shared_image_interface.h
index 9c048f7..e1621e5 100644
--- a/gpu/command_buffer/service/stream_texture_shared_image_interface.h
+++ b/gpu/command_buffer/service/stream_texture_shared_image_interface.h
@@ -10,10 +10,7 @@
 
 namespace gpu {
 class TextureOwner;
-
-namespace gles2 {
-class Texture;
-}  // namespace gles2
+class TextureBase;
 
 // This class is a specialized GLImage that lets SharedImageVideo draw video
 // frames.
@@ -32,7 +29,7 @@
   // texture.
   virtual void UpdateAndBindTexImage() = 0;
   virtual bool HasTextureOwner() const = 0;
-  virtual gles2::Texture* GetTexture() const = 0;
+  virtual TextureBase* GetTextureBase() const = 0;
 
   // Notify the texture of overlay decision, When overlay promotion is true,
   // this also sets the bounds of where the overlay is.
diff --git a/gpu/ipc/service/stream_texture_android.cc b/gpu/ipc/service/stream_texture_android.cc
index ec06b7e..be5e99b 100644
--- a/gpu/ipc/service/stream_texture_android.cc
+++ b/gpu/ipc/service/stream_texture_android.cc
@@ -159,9 +159,8 @@
   return !!texture_owner_;
 }
 
-gles2::Texture* StreamTexture::GetTexture() const {
-  DCHECK(texture_owner_);
-  return gles2::Texture::CheckedCast(texture_owner_->GetTextureBase());
+TextureBase* StreamTexture::GetTextureBase() const {
+  return texture_owner_->GetTextureBase();
 }
 
 void StreamTexture::NotifyOverlayPromotion(bool promotion,
diff --git a/gpu/ipc/service/stream_texture_android.h b/gpu/ipc/service/stream_texture_android.h
index 4f3f91f..9f364fb 100644
--- a/gpu/ipc/service/stream_texture_android.h
+++ b/gpu/ipc/service/stream_texture_android.h
@@ -93,7 +93,7 @@
   bool IsUsingGpuMemory() const override;
   void UpdateAndBindTexImage() override;
   bool HasTextureOwner() const override;
-  gles2::Texture* GetTexture() const override;
+  TextureBase* GetTextureBase() const override;
   void NotifyOverlayPromotion(bool promotion, const gfx::Rect& bounds) override;
   bool RenderToOverlay() override;
 
diff --git a/ios/build/bots/chromium.clang/ToTiOS.json b/ios/build/bots/chromium.clang/ToTiOS.json
index 297eba49..3b3712a 100644
--- a/ios/build/bots/chromium.clang/ToTiOS.json
+++ b/ios/build/bots/chromium.clang/ToTiOS.json
@@ -23,119 +23,119 @@
       "app": "base_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "boringssl_crypto_tests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "boringssl_ssl_tests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "components_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "crypto_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "gfx_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "google_apis_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "ios_chrome_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "ios_net_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "ios_web_inttests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "ios_web_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "ios_web_view_inttests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "net_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "skia_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "sql_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "ui_base_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     },
     {
       "app": "url_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "os": "13.3"
     }
   ]
diff --git a/ios/build/bots/chromium.fyi/ios-simulator-cronet.json b/ios/build/bots/chromium.fyi/ios-simulator-cronet.json
index 5351e1c..dd3680dc 100644
--- a/ios/build/bots/chromium.fyi/ios-simulator-cronet.json
+++ b/ios/build/bots/chromium.fyi/ios-simulator-cronet.json
@@ -28,14 +28,14 @@
       "device type": "iPhone X",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "app": "cronet_test",
       "device type": "iPhone X",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     }
   ]
 }
diff --git a/ios/build/bots/chromium.fyi/ios-simulator.json b/ios/build/bots/chromium.fyi/ios-simulator.json
index c50b525..7d26425 100644
--- a/ios/build/bots/chromium.fyi/ios-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios-simulator.json
@@ -19,7 +19,7 @@
       "device type": "iPhone 6s",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "app": "ios_chrome_integration_egtests",
@@ -31,7 +31,7 @@
       "xcode parallelization": true,
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "app": "ios_chrome_integration_egtests",
@@ -43,7 +43,7 @@
       "xcode parallelization": true,
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "app": "ios_chrome_integration_egtests",
@@ -52,7 +52,7 @@
       "xcode parallelization": true,
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "app": "ios_chrome_signin_egtests",
@@ -64,7 +64,7 @@
       "xcode parallelization": true,
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "app": "ios_chrome_smoke_egtests",
@@ -73,7 +73,7 @@
       "xcode parallelization": true,
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "app": "ios_chrome_web_egtests",
@@ -82,7 +82,7 @@
       "xcode parallelization": true,
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "app": "ios_web_shell_egtests",
@@ -91,7 +91,7 @@
       "xcode parallelization": true,
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "app": "ios_chrome_ui_egtests",
@@ -100,7 +100,7 @@
       "xcode parallelization": true,
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     }
   ]
 }
diff --git a/ios/build/bots/chromium.fyi/ios-webkit-tot.json b/ios/build/bots/chromium.fyi/ios-webkit-tot.json
index bbb0a813..7182e112 100644
--- a/ios/build/bots/chromium.fyi/ios-webkit-tot.json
+++ b/ios/build/bots/chromium.fyi/ios-webkit-tot.json
@@ -20,7 +20,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -31,7 +31,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -42,7 +42,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -53,7 +53,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -64,7 +64,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -75,7 +75,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -86,7 +86,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -97,7 +97,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "test args": [
         "--run-with-custom-webkit"
       ]
diff --git a/ios/build/bots/chromium.fyi/ios13-beta-simulator.json b/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
index a754ee1..7e7f3cf 100644
--- a/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
@@ -25,7 +25,7 @@
       "device type": "iPhone X",
       "os": "13.3",
       "xcode build version": "11c29",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -34,7 +34,7 @@
       "device type": "iPhone 7",
       "os": "13.3",
       "xcode build version": "11c29",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -43,7 +43,7 @@
       "device type": "iPad (6th generation)",
       "os": "13.3",
       "xcode build version": "11c29",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -52,7 +52,7 @@
       "device type": "iPhone X",
       "os": "12.4",
       "xcode build version": "11c29",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -61,7 +61,7 @@
       "device type": "iPad (6th generation)",
       "os": "12.4",
       "xcode build version": "11c29",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -70,7 +70,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "eg_cq_tests.json",
@@ -78,7 +78,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -87,7 +87,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -96,7 +96,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -105,7 +105,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -114,7 +114,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -122,7 +122,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -130,7 +130,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "app": "ios_crash_xcuitests_module",
@@ -140,7 +140,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "common_tests.json",
@@ -148,7 +148,7 @@
       "os": "12.4",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "eg_cq_tests.json",
@@ -156,7 +156,7 @@
       "os": "12.4",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -165,7 +165,7 @@
       "os": "12.4",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -174,7 +174,7 @@
       "os": "12.4",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -183,7 +183,7 @@
       "os": "12.4",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -192,7 +192,7 @@
       "os": "12.4",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -200,7 +200,7 @@
       "os": "12.4",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -208,7 +208,7 @@
       "os": "12.4",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     }
   ]
 }
diff --git a/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json b/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json
index 5bd4eda4..9668aa6 100644
--- a/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json
@@ -22,7 +22,7 @@
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s Plus",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -30,7 +30,7 @@
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -38,7 +38,7 @@
       "include": "common_tests.json",
       "device type": "iPhone 6s",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -46,7 +46,7 @@
       "include": "eg_tests.json",
       "device type": "iPhone 7",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30,
       "xcode parallelization": true
@@ -55,7 +55,7 @@
       "include": "eg_tests.json",
       "device type": "iPad Air 2",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30,
       "xcode parallelization": true
@@ -64,7 +64,7 @@
       "include": "eg_tests.json",
       "device type": "iPhone X",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30,
       "xcode parallelization": true
@@ -73,7 +73,7 @@
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30,
       "xcode parallelization": true
@@ -82,17 +82,27 @@
       "include": "eg_cq_tests.json",
       "device type": "iPad Air 2",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30,
       "xcode parallelization": true
     },
     {
+      "app": "ios_crash_xcuitests_module",
+      "host": "ios_crash_xcuitests",
+      "xcode parallelization": true,
+      "device type": "iPhone X",
+      "os": "13.3",
+      "xcode build version": "11c29",
+      "host os": "Mac-10.14.6",
+      "pool":"chromium.tests"
+    },
+    {
       "xcode parallelization": true,
       "include": "eg2_tests.json",
       "device type": "iPhone X",
       "os": "12.4",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -100,7 +110,7 @@
       "include": "eg2_tests.json",
       "device type": "iPad (6th generation)",
       "os": "12.4",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -108,14 +118,14 @@
       "device type": "iPhone X",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -123,7 +133,7 @@
       "device type": "iPad Pro (12.9-inch)",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -131,7 +141,7 @@
       "device type": "iPhone X",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -139,7 +149,7 @@
       "device type": "iPad Air (3rd generation)",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -147,21 +157,21 @@
       "device type": "iPhone 6s Plus",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone X",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPad Air 2",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     }
   ]
 }
diff --git a/ios/build/bots/chromium.mac/ios-simulator-cronet.json b/ios/build/bots/chromium.mac/ios-simulator-cronet.json
index 07f7014..3992d37 100644
--- a/ios/build/bots/chromium.mac/ios-simulator-cronet.json
+++ b/ios/build/bots/chromium.mac/ios-simulator-cronet.json
@@ -26,7 +26,7 @@
       "device type": "iPhone 6s",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "priority": 30
     },
     {
@@ -34,7 +34,7 @@
       "device type": "iPhone 6s",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "priority": 30
     },
     {
@@ -42,7 +42,7 @@
       "device type": "iPad Air 2",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "priority": 30
     },
     {
@@ -50,7 +50,7 @@
       "device type": "iPad Air 2",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests",
+      "pool": "chromium.tests.template",
       "priority": 30
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios-simulator-full-configs.json b/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
index b502acc..fbecdfc 100644
--- a/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
+++ b/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
@@ -23,7 +23,7 @@
       "device type": "iPad Air 2",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -31,7 +31,7 @@
       "device type": "iPhone X",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -39,7 +39,7 @@
       "device type": "iPad Air 2",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -47,7 +47,7 @@
       "device type": "iPhone 7",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -55,7 +55,7 @@
       "device type": "iPad Air 2",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -63,7 +63,7 @@
       "device type": "iPhone X",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -71,7 +71,7 @@
       "device type": "iPhone X",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -79,7 +79,7 @@
       "device type": "iPad Air 2",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios-simulator-noncq.json b/ios/build/bots/chromium.mac/ios-simulator-noncq.json
index f9f04ec..4adb2fa7 100644
--- a/ios/build/bots/chromium.mac/ios-simulator-noncq.json
+++ b/ios/build/bots/chromium.mac/ios-simulator-noncq.json
@@ -23,7 +23,7 @@
       "include": "eg2_tests.json",
       "device type": "iPhone X",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -31,7 +31,7 @@
       "include": "eg2_tests.json",
       "device type": "iPhone 7",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -39,7 +39,7 @@
       "include": "eg2_tests.json",
       "device type": "iPad (6th generation)",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -47,14 +47,14 @@
       "include": "eg2_tests.json",
       "device type": "iPad Air 2",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
       "app": "crashpad_tests",
       "device type": "iPhone X",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -62,7 +62,7 @@
       "include": "eg2_tests.json",
       "device type": "iPhone X",
       "os": "12.4",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -70,7 +70,7 @@
       "include": "eg2_tests.json",
       "device type": "iPhone 7",
       "os": "12.4",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -78,7 +78,7 @@
       "include": "eg2_tests.json",
       "device type": "iPad (6th generation)",
       "os": "12.4",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -86,14 +86,14 @@
       "include": "eg2_tests.json",
       "device type": "iPad Air 2",
       "os": "12.4",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
       "app": "crashpad_tests",
       "device type": "iPhone X",
       "os": "12.4",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios-simulator.json b/ios/build/bots/chromium.mac/ios-simulator.json
index 804bbd8..33c92ce 100644
--- a/ios/build/bots/chromium.mac/ios-simulator.json
+++ b/ios/build/bots/chromium.mac/ios-simulator.json
@@ -24,7 +24,7 @@
       "device type": "iPhone 6s",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -32,7 +32,7 @@
       "device type": "iPhone 6s",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -40,7 +40,7 @@
       "device type": "iPhone 6s Plus",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -48,7 +48,7 @@
       "device type": "iPhone 6s",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -56,7 +56,7 @@
       "device type": "iPhone SE",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -64,7 +64,7 @@
       "device type": "iPhone 6s",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -72,7 +72,7 @@
       "device type": "iPad Air 2",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     },
     {
@@ -80,7 +80,7 @@
       "device type": "iPhone 6s",
       "os": "13.3",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "priority": 30
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios13-beta-simulator.json b/ios/build/bots/chromium.mac/ios13-beta-simulator.json
index fd40a9c..73ebf924 100644
--- a/ios/build/bots/chromium.mac/ios13-beta-simulator.json
+++ b/ios/build/bots/chromium.mac/ios13-beta-simulator.json
@@ -25,7 +25,7 @@
       "device type": "iPhone X",
       "os": "13.3",
       "xcode build version": "11c29",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -34,7 +34,7 @@
       "device type": "iPhone 7",
       "os": "13.3",
       "xcode build version": "11c29",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -43,7 +43,7 @@
       "device type": "iPad (6th generation)",
       "os": "13.3",
       "xcode build version": "11c29",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -52,7 +52,7 @@
       "device type": "iPhone X",
       "os": "12.4",
       "xcode build version": "11c29",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -61,7 +61,7 @@
       "device type": "iPad (6th generation)",
       "os": "12.4",
       "xcode build version": "11c29",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6"
     },
     {
@@ -70,7 +70,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "eg_cq_tests.json",
@@ -78,7 +78,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -87,7 +87,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -96,7 +96,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -105,7 +105,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "xcode parallelization": true
     },
     {
@@ -114,7 +114,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -122,7 +122,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -130,7 +130,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     },
     {
       "app": "ios_crash_xcuitests_module",
@@ -140,7 +140,7 @@
       "os": "13.3",
       "xcode build version": "11c29",
       "host os": "Mac-10.14.6",
-      "pool":"chromium.tests"
+      "pool":"chromium.tests.template"
     }
   ]
 }
diff --git a/ios/build/bots/chromium.mac/ios13-sdk-simulator.json b/ios/build/bots/chromium.mac/ios13-sdk-simulator.json
index 8d90308e..1d0c962 100644
--- a/ios/build/bots/chromium.mac/ios13-sdk-simulator.json
+++ b/ios/build/bots/chromium.mac/ios13-sdk-simulator.json
@@ -25,7 +25,7 @@
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s Plus",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -33,7 +33,7 @@
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -41,7 +41,7 @@
       "include": "common_tests.json",
       "device type": "iPhone 6s",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -49,7 +49,7 @@
       "include": "eg_tests.json",
       "device type": "iPhone 7",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -57,7 +57,7 @@
       "include": "eg_tests.json",
       "device type": "iPad Air 2",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -65,7 +65,7 @@
       "include": "eg_tests.json",
       "device type": "iPhone X",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -73,7 +73,7 @@
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -81,7 +81,7 @@
       "include": "eg_cq_tests.json",
       "device type": "iPad Air 2",
       "os": "13.3",
-      "pool":"chromium.tests",
+      "pool":"chromium.tests.template",
       "host os": "Mac-10.14.6",
       "priority": 30
     }
diff --git a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
index 9024d8ac..bf77e72 100644
--- a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
+++ b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
@@ -24,21 +24,21 @@
       "device type": "iPhone 6s Plus",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests"
+      "pool": "chromium.tests.template"
     },
     {
       "include": "webrtc_tests.json",
       "device type": "iPhone 6s",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests"
+      "pool": "chromium.tests.template"
     },
     {
       "include": "webrtc_tests.json",
       "device type": "iPad Air 2",
       "os": "12.4",
       "host os": "Mac-10.14.6",
-      "pool": "chromium.tests"
+      "pool": "chromium.tests.template"
     }
   ]
 }
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index dd69d9f..385e7e2 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -505,16 +505,15 @@
   [NSURLCache setSharedURLCache:[EmptyNSURLCache emptyNSURLCache]];
 }
 
-- (void)startUpBrowserForegroundInitialization {
+// This initialization must happen before any windows are created.
+// Returns YES iff there's a session restore available.
+- (BOOL)startUpBeforeFirstWindowCreatedAndPrepareForRestorationPostCrash:
+    (BOOL)isPostCrashLaunch {
   // Give tests a chance to prepare for testing.
   tests_hook::SetUpTestsIfPresent();
 
   GetApplicationContext()->OnAppEnterForeground();
 
-  // TODO(crbug.com/546171): Audit all the following code to see if some of it
-  // should move into BrowserMainParts or BrowserProcess.
-  NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
-
   // Although this duplicates some metrics_service startup logic also in
   // IOSChromeMain(), this call does additional work, checking for wifi-only
   // and setting up the required support structures.
@@ -545,9 +544,8 @@
   // The CrashRestoreHelper must clean up the old browser state information
   // before the tabModels can be created.  |self.restoreHelper| must be kept
   // alive until the BVC receives the browser state and tab model.
-  BOOL postCrashLaunch = [self mustShowRestoreInfobar];
   BOOL needRestoration = NO;
-  if (postCrashLaunch) {
+  if (isPostCrashLaunch) {
     needRestoration = [CrashRestoreHelper
         moveAsideSessionInformationForBrowserState:chromeBrowserState];
   }
@@ -590,8 +588,36 @@
         ->ClearAllCachedSuggestions();
   }
 
-  // This is per-window code.
+  return needRestoration;
+}
 
+// This initialization must only happen once there's at least one Chrome window
+// open.
+- (void)startUpAfterFirstWindowCreated {
+  CustomizeUIAppearance();
+
+  [self scheduleStartupCleanupTasks];
+  [MetricsMediator
+      logLaunchMetricsWithStartupInformation:self
+                           interfaceProvider:self.interfaceProvider];
+  if (self.isColdStart) {
+    [ContentSuggestionsSchedulerNotifications
+        notifyColdStart:self.mainBrowserState];
+    [ContentSuggestionsSchedulerNotifications
+        notifyForeground:self.mainBrowserState];
+  }
+
+  ios::GetChromeBrowserProvider()->GetOverridesProvider()->InstallOverrides();
+
+  [self scheduleLowPriorityStartupTasks];
+
+  // Now that everything is properly set up, run the tests.
+  tests_hook::RunTestsIfPresent();
+}
+
+// Starts up a single chrome window and its UI.
+- (void)startUpChromeUIPostCrash:(BOOL)isPostCrashLaunch
+                 needRestoration:(BOOL)needsRestoration {
   DCHECK(!self.sceneController.browserViewWrangler);
   DCHECK(self.sceneController.appURLLoadingService);
 
@@ -607,7 +633,7 @@
 
   // Only create the restoration helper if the browser state was backed up
   // successfully.
-  if (needRestoration) {
+  if (needsRestoration) {
     self.restoreHelper =
         [[CrashRestoreHelper alloc] initWithBrowser:self.mainBrowser];
   }
@@ -625,10 +651,11 @@
 
   // Before bringing up the UI, make sure the launch mode is correct, and
   // check for previous crashes.
-  BOOL startInIncognito = [standardDefaults boolForKey:kIncognitoCurrentKey];
+  BOOL startInIncognito =
+      [[NSUserDefaults standardUserDefaults] boolForKey:kIncognitoCurrentKey];
   BOOL switchFromIncognito = startInIncognito && ![self canLaunchInIncognito];
 
-  if (postCrashLaunch || switchFromIncognito) {
+  if (isPostCrashLaunch || switchFromIncognito) {
     [self.sceneController clearIOSSpecificIncognitoData];
     if (switchFromIncognito)
       [self.sceneController.browserViewWrangler
@@ -650,28 +677,15 @@
     [self.restoreHelper showRestorePrompt];
     self.restoreHelper = nil;
   }
+}
 
-  // End of per-window code.
-
-  CustomizeUIAppearance();
-
-  [self scheduleStartupCleanupTasks];
-  [MetricsMediator
-      logLaunchMetricsWithStartupInformation:self
-                           interfaceProvider:self.interfaceProvider];
-  if (self.isColdStart) {
-    [ContentSuggestionsSchedulerNotifications
-        notifyColdStart:self.mainBrowserState];
-    [ContentSuggestionsSchedulerNotifications
-        notifyForeground:self.mainBrowserState];
-  }
-
-  ios::GetChromeBrowserProvider()->GetOverridesProvider()->InstallOverrides();
-
-  [self scheduleLowPriorityStartupTasks];
-
-  // Now that everything is properly set up, run the tests.
-  tests_hook::RunTestsIfPresent();
+- (void)startUpBrowserForegroundInitialization {
+  BOOL postCrashLaunch = [self mustShowRestoreInfobar];
+  BOOL needRestore =
+      [self startUpBeforeFirstWindowCreatedAndPrepareForRestorationPostCrash:
+                postCrashLaunch];
+  [self startUpChromeUIPostCrash:postCrashLaunch needRestoration:needRestore];
+  [self startUpAfterFirstWindowCreated];
 }
 
 - (void)initializeBrowserState:(ChromeBrowserState*)browserState {
diff --git a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm
index 47c2a13..e02d458 100644
--- a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm
@@ -36,6 +36,9 @@
     UnifiedConsentCoordinator* unifiedConsentCoordinator;
 // Coordinator that handles adding a user account.
 @property(nonatomic, strong) SigninCoordinator* addAccountSigninCoordinator;
+// Coordinator that handles the advanced settings sign-in.
+@property(nonatomic, strong)
+    SigninCoordinator* advancedSettingsSigninCoordinator;
 // View controller that handles the sign-in UI.
 @property(nonatomic, strong) UserSigninViewController* viewController;
 // Mediator that handles the sign-in authentication state.
@@ -109,40 +112,51 @@
 
 - (void)interruptWithAction:(SigninCoordinatorInterruptAction)action
                  completion:(ProceduralBlock)completion {
-  [self.mediator cancelAndDismissAuthenticationFlow];
-
-  ProceduralBlock runCompletionCallback = ^{
-    [self
-        runCompletionCallbackWithSigninResult:SigninCoordinatorResultInterrupted
-                                     identity:self.unifiedConsentCoordinator
-                                                  .selectedIdentity];
-    if (completion) {
-      completion();
-    }
-  };
-  switch (action) {
-    case SigninCoordinatorInterruptActionNoDismiss: {
-      runCompletionCallback();
-      break;
-    }
-    case SigninCoordinatorInterruptActionDismissWithAnimation: {
-      [self.viewController dismissViewControllerAnimated:YES
-                                              completion:runCompletionCallback];
-      break;
-    }
-    case SigninCoordinatorInterruptActionDismissWithoutAnimation: {
-      [self.viewController dismissViewControllerAnimated:NO
-                                              completion:runCompletionCallback];
-      break;
-    }
+  __weak UserSigninCoordinator* weakSelf = self;
+  if (self.addAccountSigninCoordinator) {
+    // |self.addAccountSigninCoordinator| needs to be interupted before
+    // interrupting |self.viewController|.
+    // The add account view should not be dismissed since the
+    // |self.viewController| will take care of that according to |action|.
+    [self.addAccountSigninCoordinator
+        interruptWithAction:SigninCoordinatorInterruptActionNoDismiss
+                 completion:^{
+                   // |self.addAccountSigninCoordinator.signinCompletion|
+                   // is expected to be called before this block.
+                   // Therefore |weakSelf.addAccountSigninCoordinator| is
+                   // expected to be nil.
+                   DCHECK(!weakSelf.addAccountSigninCoordinator);
+                   [weakSelf interruptUserSigninUIWithAction:action
+                                                  completion:completion];
+                 }];
+    return;
+  } else if (self.advancedSettingsSigninCoordinator) {
+    // |self.viewController| has already been dismissed. The interruption should
+    // be sent to |self.advancedSettingsSigninCoordinator|.
+    DCHECK(!self.viewController);
+    DCHECK(!self.mediator);
+    [self.advancedSettingsSigninCoordinator
+        interruptWithAction:action
+                 completion:^{
+                   // |self.advancedSettingsSigninCoordinator.signinCompletion|
+                   // is expected to be called before this block.
+                   // Therefore |weakSelf.advancedSettingsSigninCoordinator| is
+                   // expected to be nil.
+                   DCHECK(!weakSelf.advancedSettingsSigninCoordinator);
+                   if (completion) {
+                     completion();
+                   }
+                 }];
+    return;
   }
+  [self interruptUserSigninUIWithAction:action completion:completion];
 }
 
 #pragma mark - UnifiedConsentCoordinatorDelegate
 
 - (void)unifiedConsentCoordinatorDidTapSettingsLink:
     (UnifiedConsentCoordinator*)coordinator {
-  // TODO(crbug.com/971989): Needs implementation.
+  [self startSigninFlow];
 }
 
 - (void)unifiedConsentCoordinatorDidReachBottom:
@@ -196,20 +210,7 @@
 }
 
 - (void)userSigninViewControllerDidTapOnSignin {
-  DCHECK(self.unifiedConsentCoordinator.selectedIdentity);
-  // TODO(crbug.com/971989): Pass ShouldClearDataOption from main controller.
-  AuthenticationFlow* authenticationFlow = [[AuthenticationFlow alloc]
-               initWithBrowser:self.browser
-                      identity:self.unifiedConsentCoordinator.selectedIdentity
-               shouldClearData:SHOULD_CLEAR_DATA_USER_CHOICE
-              postSignInAction:POST_SIGNIN_ACTION_NONE
-      presentingViewController:self.viewController];
-  authenticationFlow.dispatcher = HandlerForProtocol(
-      self.browser->GetCommandDispatcher(), BrowsingDataCommands);
-
-  [self.mediator
-      authenticateWithIdentity:self.unifiedConsentCoordinator.selectedIdentity
-            authenticationFlow:authenticationFlow];
+  [self startSigninFlow];
 }
 
 #pragma mark - UserSigninMediatorDelegate
@@ -240,21 +241,16 @@
 
 - (void)userSigninMediatorSigninFinishedWithResult:
     (SigninCoordinatorResult)signinResult {
+  BOOL settingsWasTapped = self.unifiedConsentCoordinator.settingsLinkWasTapped;
+  ChromeIdentity* identity = self.unifiedConsentCoordinator.selectedIdentity;
   [self recordSigninMetricsWithResult:signinResult];
-
   __weak UserSigninCoordinator* weakSelf = self;
   ProceduralBlock completion = ^void() {
-    [weakSelf
-        runCompletionCallbackWithSigninResult:signinResult
-                                     identity:weakSelf.unifiedConsentCoordinator
-                                                  .selectedIdentity];
+    [weakSelf viewControllerDismissedWithResult:signinResult
+                                       identity:identity
+                          settingsLinkWasTapped:settingsWasTapped];
   };
-
   [self.viewController dismissViewControllerAnimated:YES completion:completion];
-
-  self.unifiedConsentCoordinator.delegate = nil;
-  [self.unifiedConsentCoordinator stop];
-  self.unifiedConsentCoordinator = nil;
 }
 
 - (void)userSigninMediatorNeedPrimaryButtonUpdate {
@@ -263,6 +259,43 @@
 
 #pragma mark - Private
 
+// Called when |self.viewController| is dismissed. If |settingsWasTapped| is
+// NO, the sign-in is finished and
+// |runCompletionCallbackWithSigninResult:identity:| is called.
+// Otherwise, the advanced settings sign-in is presented.
+- (void)viewControllerDismissedWithResult:(SigninCoordinatorResult)signinResult
+                                 identity:(ChromeIdentity*)identity
+                    settingsLinkWasTapped:(BOOL)settingsWasTapped {
+  DCHECK(!self.addAccountSigninCoordinator);
+  DCHECK(!self.advancedSettingsSigninCoordinator);
+  DCHECK(self.unifiedConsentCoordinator);
+  DCHECK(self.mediator);
+  DCHECK(self.viewController);
+  [self.unifiedConsentCoordinator stop];
+  self.unifiedConsentCoordinator = nil;
+  self.mediator = nil;
+  self.viewController = nil;
+  if (!settingsWasTapped) {
+    [self runCompletionCallbackWithSigninResult:signinResult identity:identity];
+    return;
+  }
+  self.advancedSettingsSigninCoordinator = [SigninCoordinator
+      advancedSettingsSigninCoordinatorWithBaseViewController:
+          self.baseViewController
+                                                      browser:self.browser];
+  __weak UserSigninCoordinator* weakSelf = self;
+  self.advancedSettingsSigninCoordinator.signinCompletion = ^(
+      SigninCoordinatorResult advancedSigninResult,
+      ChromeIdentity* advancedSigninIdentity) {
+    [weakSelf
+        advancedSettingsSigninCoordinatorFinishedWithResult:advancedSigninResult
+                                                   identity:
+                                                       advancedSigninIdentity];
+  };
+  [self.advancedSettingsSigninCoordinator start];
+}
+
+// Records the metrics when the sign-in is finished.
 - (void)recordSigninMetricsWithResult:(SigninCoordinatorResult)signinResult {
   switch (signinResult) {
     case SigninCoordinatorResultSuccess: {
@@ -282,4 +315,76 @@
   }
 }
 
+// Interrupts the sign-in when |self.viewController| is presented, by dismissing
+// it if needed (according to |action|). Then |completion| is called.
+// This method should not be called if |self.addAccountSigninCoordinator| has
+// not been stopped before.
+- (void)interruptUserSigninUIWithAction:(SigninCoordinatorInterruptAction)action
+                             completion:(ProceduralBlock)completion {
+  DCHECK(self.viewController);
+  DCHECK(self.mediator);
+  DCHECK(self.unifiedConsentCoordinator);
+  DCHECK(!self.addAccountSigninCoordinator);
+  DCHECK(!self.advancedSettingsSigninCoordinator);
+  [self.mediator cancelAndDismissAuthenticationFlow];
+  __weak UserSigninCoordinator* weakSelf = self;
+  ProceduralBlock runCompletionCallback = ^{
+    [weakSelf
+        runCompletionCallbackWithSigninResult:SigninCoordinatorResultInterrupted
+                                     identity:self.unifiedConsentCoordinator
+                                                  .selectedIdentity];
+    if (completion) {
+      completion();
+    }
+  };
+  switch (action) {
+    case SigninCoordinatorInterruptActionNoDismiss: {
+      runCompletionCallback();
+      break;
+    }
+    case SigninCoordinatorInterruptActionDismissWithAnimation: {
+      [self.viewController dismissViewControllerAnimated:YES
+                                              completion:runCompletionCallback];
+      break;
+    }
+    case SigninCoordinatorInterruptActionDismissWithoutAnimation: {
+      [self.viewController dismissViewControllerAnimated:NO
+                                              completion:runCompletionCallback];
+      break;
+    }
+  }
+}
+
+// Triggers the sign-in workflow.
+- (void)startSigninFlow {
+  DCHECK(self.unifiedConsentCoordinator);
+  DCHECK(self.unifiedConsentCoordinator.selectedIdentity);
+  // TODO(crbug.com/971989): Pass ShouldClearDataOption from main controller.
+  AuthenticationFlow* authenticationFlow = [[AuthenticationFlow alloc]
+               initWithBrowser:self.browser
+                      identity:self.unifiedConsentCoordinator.selectedIdentity
+               shouldClearData:SHOULD_CLEAR_DATA_USER_CHOICE
+              postSignInAction:POST_SIGNIN_ACTION_NONE
+      presentingViewController:self.viewController];
+  authenticationFlow.dispatcher = HandlerForProtocol(
+      self.browser->GetCommandDispatcher(), BrowsingDataCommands);
+
+  [self.mediator
+      authenticateWithIdentity:self.unifiedConsentCoordinator.selectedIdentity
+            authenticationFlow:authenticationFlow];
+}
+
+// Triggers |self.signinCompletion| by calling
+// |runCompletionCallbackWithSigninResult:identity:| when
+// |self.advancedSettingsSigninCoordinator| is done.
+- (void)advancedSettingsSigninCoordinatorFinishedWithResult:
+            (SigninCoordinatorResult)signinResult
+                                                   identity:(ChromeIdentity*)
+                                                                identity {
+  DCHECK(self.advancedSettingsSigninCoordinator);
+  [self.advancedSettingsSigninCoordinator stop];
+  self.advancedSettingsSigninCoordinator = nil;
+  [self runCompletionCallbackWithSigninResult:signinResult identity:identity];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/toolbar/keyboard_assist/BUILD.gn b/ios/chrome/browser/ui/toolbar/keyboard_assist/BUILD.gn
index 501df904..c890f32 100644
--- a/ios/chrome/browser/ui/toolbar/keyboard_assist/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/keyboard_assist/BUILD.gn
@@ -17,6 +17,10 @@
     "toolbar_keyboard_accessory_view.mm",
     "toolbar_ui_bar_button_item.h",
     "toolbar_ui_bar_button_item.mm",
+    "voice_search_keyboard_accessory_button.h",
+    "voice_search_keyboard_accessory_button.mm",
+    "voice_search_keyboard_bar_button_item.h",
+    "voice_search_keyboard_bar_button_item.mm",
   ]
   deps = [
     "resources:keyboard_accessory_qr_scanner",
@@ -42,3 +46,20 @@
       [ "//ios/chrome/browser/ui/omnibox:omnibox_internal" ]
   libs = [ "UIKit.framework" ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "voice_search_keyboard_accessory_button_unittest.mm",
+    "voice_search_keyboard_bar_button_item_unittest.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    ":keyboard_assist",
+    "//ios/chrome/browser/voice",
+    "//ios/chrome/browser/voice:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_assistive_keyboard_views_utils.mm b/ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_assistive_keyboard_views_utils.mm
index b94b366..2bbc50fa 100644
--- a/ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_assistive_keyboard_views_utils.mm
+++ b/ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_assistive_keyboard_views_utils.mm
@@ -6,7 +6,9 @@
 
 #include "base/logging.h"
 #import "ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_assistive_keyboard_delegate.h"
+#import "ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/voice/voice_search_availability.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -21,12 +23,11 @@
 
 namespace {
 
-UIButton* ButtonWithIcon(NSString* iconName) {
+void SetUpButtonWithIcon(UIButton* button, NSString* iconName) {
   const CGFloat kButtonShadowOpacity = 0.35;
   const CGFloat kButtonShadowRadius = 1.0;
   const CGFloat kButtonShadowVerticalOffset = 1.0;
 
-  UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
   [button setTranslatesAutoresizingMaskIntoConstraints:NO];
   UIImage* icon = [UIImage imageNamed:iconName];
   [button setImage:icon forState:UIControlStateNormal];
@@ -34,15 +35,18 @@
   button.layer.shadowOffset = CGSizeMake(0, kButtonShadowVerticalOffset);
   button.layer.shadowOpacity = kButtonShadowOpacity;
   button.layer.shadowRadius = kButtonShadowRadius;
-  return button;
 }
 
 }  // namespace
 
 NSArray<UIButton*>* ToolbarAssistiveKeyboardLeadingButtons(
     id<ToolbarAssistiveKeyboardDelegate> delegate) {
-  UIButton* voiceSearchButton =
-      ButtonWithIcon(@"keyboard_accessory_voice_search");
+  NSMutableArray<UIButton*>* buttons = [NSMutableArray<UIButton*> array];
+
+  UIButton* voiceSearchButton = [[VoiceSearchKeyboardAccessoryButton alloc]
+      initWithVoiceSearchAvailability:std::make_unique<
+                                          VoiceSearchAvailability>()];
+  SetUpButtonWithIcon(voiceSearchButton, @"keyboard_accessory_voice_search");
   NSString* accessibilityLabel =
       l10n_util::GetNSString(IDS_IOS_KEYBOARD_ACCESSORY_VIEW_VOICE_SEARCH);
   voiceSearchButton.accessibilityLabel = accessibilityLabel;
@@ -51,15 +55,17 @@
              addTarget:delegate
                 action:@selector(keyboardAccessoryVoiceSearchTouchUpInside:)
       forControlEvents:UIControlEventTouchUpInside];
+  [buttons addObject:voiceSearchButton];
 
-  UIButton* cameraButton = ButtonWithIcon(@"keyboard_accessory_qr_scanner");
+  UIButton* cameraButton = [UIButton buttonWithType:UIButtonTypeCustom];
+  SetUpButtonWithIcon(cameraButton, @"keyboard_accessory_qr_scanner");
   [cameraButton addTarget:delegate
                    action:@selector(keyboardAccessoryCameraSearchTouchUp)
          forControlEvents:UIControlEventTouchUpInside];
   SetA11yLabelAndUiAutomationName(
       cameraButton, IDS_IOS_KEYBOARD_ACCESSORY_VIEW_QR_CODE_SEARCH,
       @"QR code Search");
+  [buttons addObject:cameraButton];
 
-  NSArray<UIButton*>* buttons = @[ voiceSearchButton, cameraButton ];
   return buttons;
 }
diff --git a/ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_input_assistant_items.mm b/ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_input_assistant_items.mm
index c522f4f..c847682e 100644
--- a/ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_input_assistant_items.mm
+++ b/ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_input_assistant_items.mm
@@ -8,6 +8,7 @@
 #import "ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_assistive_keyboard_views.h"
 #import "ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_assistive_keyboard_views_utils.h"
 #import "ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_ui_bar_button_item.h"
+#import "ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/voice/voice_search_availability.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -18,25 +19,27 @@
 #error "This file requires ARC support."
 #endif
 
+#pragma mark - Util Functions
+
 NSArray<UIBarButtonItemGroup*>* ToolbarAssistiveKeyboardLeadingBarButtonGroups(
     id<ToolbarAssistiveKeyboardDelegate> delegate) {
   NSMutableArray<UIBarButtonItem*>* items = [NSMutableArray array];
-  VoiceSearchAvailability voice_search_availability;
-  if (voice_search_availability.IsVoiceSearchAvailable()) {
-    UIImage* voiceSearchIcon =
-        [[UIImage imageNamed:@"keyboard_accessory_voice_search"]
-            imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
-    UIBarButtonItem* voiceSearchItem = [[UIBarButtonItem alloc]
-        initWithImage:voiceSearchIcon
-                style:UIBarButtonItemStylePlain
-               target:delegate
-               action:@selector(keyboardAccessoryVoiceSearchTouchUpInside:)];
-    NSString* accessibilityLabel =
-        l10n_util::GetNSString(IDS_IOS_KEYBOARD_ACCESSORY_VIEW_VOICE_SEARCH);
-    voiceSearchItem.accessibilityLabel = accessibilityLabel;
-    voiceSearchItem.accessibilityIdentifier = kVoiceSearchInputAccessoryViewID;
-    [items addObject:voiceSearchItem];
-  }
+
+  UIImage* voiceSearchIcon =
+      [[UIImage imageNamed:@"keyboard_accessory_voice_search"]
+          imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
+  UIBarButtonItem* voiceSearchItem = [[VoiceSearchKeyboardBarButtonItem alloc]
+                initWithImage:voiceSearchIcon
+                        style:UIBarButtonItemStylePlain
+                       target:delegate
+                       action:@selector
+                       (keyboardAccessoryVoiceSearchTouchUpInside:)
+      voiceSearchAvailability:std::make_unique<VoiceSearchAvailability>()];
+  NSString* accessibilityLabel =
+      l10n_util::GetNSString(IDS_IOS_KEYBOARD_ACCESSORY_VIEW_VOICE_SEARCH);
+  voiceSearchItem.accessibilityLabel = accessibilityLabel;
+  voiceSearchItem.accessibilityIdentifier = kVoiceSearchInputAccessoryViewID;
+  [items addObject:voiceSearchItem];
 
   UIImage* cameraIcon = [[UIImage imageNamed:@"keyboard_accessory_qr_scanner"]
       imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
@@ -50,9 +53,10 @@
       @"QR code Search");
   [items addObject:cameraItem];
 
-  UIBarButtonItemGroup* group =
-      [[UIBarButtonItemGroup alloc] initWithBarButtonItems:items
-                                        representativeItem:nil];
+  UIBarButtonItemGroup* group = [[UIBarButtonItemGroup alloc]
+      initWithBarButtonItems:@[ voiceSearchItem, cameraItem ]
+          representativeItem:nil];
+
   return @[ group ];
 }
 
diff --git a/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.h b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.h
new file mode 100644
index 0000000..22f9f37
--- /dev/null
+++ b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.h
@@ -0,0 +1,24 @@
+// 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_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_ACCESSORY_BUTTON_H_
+#define IOS_CHROME_BROWSER_UI_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_ACCESSORY_BUTTON_H_
+
+#import <UIKit/UIKit.h>
+#include <memory>
+
+class VoiceSearchAvailability;
+
+// A custom button that disables itself when voice search becomes unavailable.
+@interface VoiceSearchKeyboardAccessoryButton : UIButton
+
+- (instancetype)initWithVoiceSearchAvailability:
+    (std::unique_ptr<VoiceSearchAvailability>)availability
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
+- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_ACCESSORY_BUTTON_H_
diff --git a/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.mm b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.mm
new file mode 100644
index 0000000..a243117
--- /dev/null
+++ b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.mm
@@ -0,0 +1,57 @@
+// 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/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.h"
+
+#include "base/logging.h"
+#import "ios/chrome/browser/voice/voice_search_availability.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface VoiceSearchKeyboardAccessoryButton () <
+    VoiceSearchAvailabilityObserver> {
+  std::unique_ptr<VoiceSearchAvailability> _availability;
+}
+@end
+
+@implementation VoiceSearchKeyboardAccessoryButton
+
+- (instancetype)initWithVoiceSearchAvailability:
+    (std::unique_ptr<VoiceSearchAvailability>)availability {
+  if (self = [super initWithFrame:CGRectZero]) {
+    _availability = std::move(availability);
+    DCHECK(_availability);
+    _availability->AddObserver(self);
+  }
+  return self;
+}
+
+- (void)dealloc {
+  _availability->RemoveObserver(self);
+}
+
+#pragma mark - UIView
+
+- (void)willMoveToSuperview:(UIView*)newSuperview {
+  [self updateEnabledState];
+}
+
+#pragma mark - VoiceSearchAvailabilityObserver
+
+- (void)voiceSearchAvailability:(VoiceSearchAvailability*)availability
+            updatedAvailability:(BOOL)available {
+  [self updateEnabledState];
+}
+
+#pragma mark - Private
+
+// Updates the button's enabled state according to its voice search
+// availability.
+- (void)updateEnabledState {
+  self.enabled = _availability->IsVoiceSearchAvailable();
+}
+
+@end
diff --git a/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button_unittest.mm b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button_unittest.mm
new file mode 100644
index 0000000..e4c96fe
--- /dev/null
+++ b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button_unittest.mm
@@ -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.
+
+#import "ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.h"
+
+#import "ios/chrome/browser/voice/fake_voice_search_availability.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test fixture for VoiceSearchKeyboardAccessoryButton.
+class VoiceSearchKeyboardAccessoryButtonTest : public PlatformTest {
+ public:
+  VoiceSearchKeyboardAccessoryButtonTest()
+      : superview_([[UIView alloc] initWithFrame:CGRectZero]) {
+    std::unique_ptr<FakeVoiceSearchAvailability> availability =
+        std::make_unique<FakeVoiceSearchAvailability>();
+    availability_ = availability.get();
+    availability_->SetVoiceOverEnabled(false);
+    availability_->SetVoiceProviderEnabled(true);
+    button_ = [[VoiceSearchKeyboardAccessoryButton alloc]
+        initWithVoiceSearchAvailability:std::move(availability)];
+    [superview_ addSubview:button_];
+  }
+
+ protected:
+  FakeVoiceSearchAvailability* availability_ = nullptr;
+  UIView* superview_ = nil;
+  VoiceSearchKeyboardAccessoryButton* button_ = nil;
+};
+
+// Tests that the button is disabled when VoiceOver is enabled.
+TEST_F(VoiceSearchKeyboardAccessoryButtonTest, DisableForVoiceOver) {
+  ASSERT_TRUE(button_.enabled);
+
+  availability_->SetVoiceOverEnabled(true);
+  EXPECT_FALSE(button_.enabled);
+
+  availability_->SetVoiceOverEnabled(false);
+  EXPECT_TRUE(button_.enabled);
+}
diff --git a/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.h b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.h
new file mode 100644
index 0000000..5adb7f26
--- /dev/null
+++ b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.h
@@ -0,0 +1,31 @@
+// 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_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_BAR_BUTTON_ITEM_H_
+#define IOS_CHROME_BROWSER_UI_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_BAR_BUTTON_ITEM_H_
+
+#import <UIKit/UIKit.h>
+#include <memory>
+
+class VoiceSearchAvailability;
+
+// A custom bar button item that disables itself when voice search is
+// unavailable.
+@interface VoiceSearchKeyboardBarButtonItem : UIBarButtonItem
+
+// Initializer for an item that disables itself when |availability| returns
+// false for IsVoiceSearchAvailable().
+- (instancetype)initWithImage:(UIImage*)image
+                        style:(UIBarButtonItemStyle)style
+                       target:(id)target
+                       action:(SEL)action
+      voiceSearchAvailability:
+          (std::unique_ptr<VoiceSearchAvailability>)availability
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_BAR_BUTTON_ITEM_H_
diff --git a/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.mm b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.mm
new file mode 100644
index 0000000..e2a26c4
--- /dev/null
+++ b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.mm
@@ -0,0 +1,57 @@
+// 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/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.h"
+
+#import "ios/chrome/browser/voice/voice_search_availability.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface VoiceSearchKeyboardBarButtonItem () <
+    VoiceSearchAvailabilityObserver> {
+  std::unique_ptr<VoiceSearchAvailability> _availability;
+}
+@end
+
+@implementation VoiceSearchKeyboardBarButtonItem
+
+- (instancetype)initWithImage:(UIImage*)image
+                        style:(UIBarButtonItemStyle)style
+                       target:(id)target
+                       action:(SEL)action
+      voiceSearchAvailability:
+          (std::unique_ptr<VoiceSearchAvailability>)availability {
+  if (self = [super init]) {
+    self.image = image;
+    self.style = style;
+    self.target = target;
+    self.action = action;
+    _availability = std::move(availability);
+    _availability->AddObserver(self);
+    [self updateEnabledState];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  _availability->RemoveObserver(self);
+}
+
+#pragma mark - VoiceSearchAvailabilityObserver
+
+- (void)voiceSearchAvailability:(VoiceSearchAvailability*)availability
+            updatedAvailability:(BOOL)available {
+  [self updateEnabledState];
+}
+
+#pragma mark - Private
+
+// Updates the item's enabled state according to its voice search availability.
+- (void)updateEnabledState {
+  self.enabled = _availability->IsVoiceSearchAvailable();
+}
+
+@end
diff --git a/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item_unittest.mm b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item_unittest.mm
new file mode 100644
index 0000000..6955d25
--- /dev/null
+++ b/ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item_unittest.mm
@@ -0,0 +1,45 @@
+// 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/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.h"
+
+#import "ios/chrome/browser/voice/fake_voice_search_availability.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test fixture for VoiceSearchKeyboardBarButtonItem.
+class VoiceSearchKeyboardBarButtonItemTest : public PlatformTest {
+ public:
+  VoiceSearchKeyboardBarButtonItemTest() {
+    std::unique_ptr<FakeVoiceSearchAvailability> availability =
+        std::make_unique<FakeVoiceSearchAvailability>();
+    availability_ = availability.get();
+    availability_->SetVoiceOverEnabled(false);
+    availability_->SetVoiceProviderEnabled(true);
+    item_ = [[VoiceSearchKeyboardBarButtonItem alloc]
+                  initWithImage:nil
+                          style:UIBarButtonItemStylePlain
+                         target:nil
+                         action:nil
+        voiceSearchAvailability:std::move(availability)];
+  }
+
+ protected:
+  FakeVoiceSearchAvailability* availability_ = nullptr;
+  VoiceSearchKeyboardBarButtonItem* item_ = nil;
+};
+
+// Tests that the item is disabled when VoiceOver is enabled.
+TEST_F(VoiceSearchKeyboardBarButtonItemTest, DisableForVoiceOver) {
+  ASSERT_TRUE(item_.enabled);
+
+  availability_->SetVoiceOverEnabled(true);
+  EXPECT_FALSE(item_.enabled);
+
+  availability_->SetVoiceOverEnabled(false);
+  EXPECT_TRUE(item_.enabled);
+}
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 5f90cf0..8d573c2 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -295,6 +295,7 @@
     "//ios/chrome/browser/ui/tabs:unit_tests",
     "//ios/chrome/browser/ui/toolbar:unit_tests",
     "//ios/chrome/browser/ui/toolbar/fullscreen:unit_tests",
+    "//ios/chrome/browser/ui/toolbar/keyboard_assist:unit_tests",
     "//ios/chrome/browser/ui/toolbar_container:unit_tests",
     "//ios/chrome/browser/ui/translate:unit_tests",
     "//ios/chrome/browser/ui/util:unit_tests",
diff --git a/media/audio/pulse/pulse_input.cc b/media/audio/pulse/pulse_input.cc
index ed42cdb..3f0c9d7 100644
--- a/media/audio/pulse/pulse_input.cc
+++ b/media/audio/pulse/pulse_input.cc
@@ -15,22 +15,6 @@
 
 namespace media {
 
-namespace {
-
-PRINTF_FORMAT(2, 3)
-void SendLogMessage(const AudioManagerBase::LogCallback& callback,
-                    const char* format,
-                    ...) {
-  if (callback.is_null())
-    return;
-  va_list args;
-  va_start(args, format);
-  callback.Run("PAIS::" + base::StringPrintV(format, args));
-  va_end(args);
-}
-
-}  // namespace
-
 using pulse::AutoPulseLock;
 using pulse::WaitForOperationCompletion;
 
@@ -62,7 +46,7 @@
   DCHECK(mainloop);
   DCHECK(context);
   CHECK(params_.IsValid());
-  SendLogMessage(log_callback_, "%s({device_id=%s}, {params=[%s]})", __func__,
+  SendLogMessage("%s({device_id=%s}, {params=[%s]})", __func__,
                  device_name.c_str(), params.AsHumanReadableString().c_str());
 }
 
@@ -74,19 +58,17 @@
 
 bool PulseAudioInputStream::Open() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  SendLogMessage(log_callback_, "%s()", __func__);
+  SendLogMessage("%s()", __func__);
   if (device_name_ == AudioDeviceDescription::kDefaultDeviceId &&
       audio_manager_->DefaultSourceIsMonitor()) {
-    SendLogMessage(log_callback_, "%s => (ERROR: can't open monitor device)",
-                   __func__);
+    SendLogMessage("%s => (ERROR: can't open monitor device)", __func__);
     return false;
   }
 
   AutoPulseLock auto_lock(pa_mainloop_);
   if (!pulse::CreateInputStream(pa_mainloop_, pa_context_, &handle_, params_,
                                 device_name_, &StreamNotifyCallback, this)) {
-    SendLogMessage(log_callback_, "%s => (ERROR: failed to open PA stream)",
-                   __func__);
+    SendLogMessage("%s => (ERROR: failed to open PA stream)", __func__);
     return false;
   }
 
@@ -99,7 +81,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(callback);
   DCHECK(handle_);
-  SendLogMessage(log_callback_, "%s()", __func__);
+  SendLogMessage("%s()", __func__);
 
   // AGC needs to be started out of the lock.
   StartAgc();
@@ -126,7 +108,7 @@
 
 void PulseAudioInputStream::Stop() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  SendLogMessage(log_callback_, "%s()", __func__);
+  SendLogMessage("%s()", __func__);
   AutoPulseLock auto_lock(pa_mainloop_);
   if (!stream_started_)
     return;
@@ -160,7 +142,7 @@
 
 void PulseAudioInputStream::Close() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  SendLogMessage(log_callback_, "%s()", __func__);
+  SendLogMessage("%s()", __func__);
   {
     AutoPulseLock auto_lock(pa_mainloop_);
     if (handle_) {
@@ -192,7 +174,7 @@
   AutoPulseLock auto_lock(pa_mainloop_);
   if (!handle_)
     return;
-  SendLogMessage(log_callback_, "%s({volume=%.2f})", __func__, volume);
+  SendLogMessage("%s({volume=%.2f})", __func__, volume);
 
   size_t index = pa_stream_get_device_index(handle_);
   pa_operation* operation = nullptr;
@@ -204,8 +186,7 @@
     if (!WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
                                     handle_) ||
         !channels_) {
-      SendLogMessage(log_callback_,
-                     "%s => (WARNING: failed to read number of channels)",
+      SendLogMessage("%s => (WARNING: failed to read number of channels)",
                      __func__);
       return;
     }
@@ -252,6 +233,15 @@
   // Not supported. Do nothing.
 }
 
+void PulseAudioInputStream::SendLogMessage(const char* format, ...) {
+  if (log_callback_.is_null())
+    return;
+  va_list args;
+  va_start(args, format);
+  log_callback_.Run("PAIS::" + base::StringPrintV(format, args));
+  va_end(args);
+}
+
 // static, used by pa_stream_set_read_callback.
 void PulseAudioInputStream::ReadCallback(pa_stream* handle,
                                          size_t length,
diff --git a/media/audio/pulse/pulse_input.h b/media/audio/pulse/pulse_input.h
index 6287ec8..2fe4b77 100644
--- a/media/audio/pulse/pulse_input.h
+++ b/media/audio/pulse/pulse_input.h
@@ -45,6 +45,9 @@
   void SetOutputDeviceForAec(const std::string& output_device_id) override;
 
  private:
+  // Helper method used for sending native logs to the registered client.
+  void SendLogMessage(const char* format, ...) PRINTF_FORMAT(2, 3);
+
   // PulseAudio Callbacks.
   static void ReadCallback(pa_stream* handle, size_t length, void* user_data);
   static void StreamNotifyCallback(pa_stream* stream, void* user_data);
diff --git a/media/audio/pulse/pulse_output.cc b/media/audio/pulse/pulse_output.cc
index 40a390c..cdcda5f 100644
--- a/media/audio/pulse/pulse_output.cc
+++ b/media/audio/pulse/pulse_output.cc
@@ -18,22 +18,6 @@
 
 namespace media {
 
-namespace {
-
-PRINTF_FORMAT(2, 3)
-void SendLogMessage(const AudioManagerBase::LogCallback& callback,
-                    const char* format,
-                    ...) {
-  if (callback.is_null())
-    return;
-  va_list args;
-  va_start(args, format);
-  callback.Run("PAOS::" + base::StringPrintV(format, args));
-  va_end(args);
-}
-
-}  // namespace
-
 using pulse::AutoPulseLock;
 using pulse::WaitForOperationCompletion;
 
@@ -78,10 +62,8 @@
       source_callback_(nullptr),
       buffer_size_(params_.GetBytesPerBuffer(kSampleFormatF32)) {
   CHECK(params_.IsValid());
-  SendLogMessage(
-      log_callback_,
-      "PulseAudioOutputStream({device_id=%s}, {params=[%s]} [this=%p])",
-      device_id.c_str(), params.AsHumanReadableString().c_str(), this);
+  SendLogMessage("%s({device_id=%s}, {params=[%s]})", __func__,
+                 device_id.c_str(), params.AsHumanReadableString().c_str());
   audio_bus_ = AudioBus::Create(params_);
 }
 
@@ -95,13 +77,13 @@
 
 bool PulseAudioOutputStream::Open() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  SendLogMessage(log_callback_, "Open([this=%p])", this);
+  SendLogMessage("%s()", __func__);
   bool result = pulse::CreateOutputStream(
       &pa_mainloop_, &pa_context_, &pa_stream_, params_, device_id_,
       AudioManager::GetGlobalAppName(), &StreamNotifyCallback,
       &StreamRequestCallback, this);
   if (!result) {
-    SendLogMessage(log_callback_, "Open => (ERROR: failed to open PA stream)");
+    SendLogMessage("%s => (ERROR: failed to open PA stream)", __func__);
   }
   return result;
 }
@@ -147,7 +129,7 @@
 
 void PulseAudioOutputStream::Close() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  SendLogMessage(log_callback_, "Close([this=%p])", this);
+  SendLogMessage("%s()", __func__);
 
   Reset();
 
@@ -160,6 +142,16 @@
 // sufficient to simply always flush upon Start().
 void PulseAudioOutputStream::Flush() {}
 
+void PulseAudioOutputStream::SendLogMessage(const char* format, ...) {
+  if (log_callback_.is_null())
+    return;
+  va_list args;
+  va_start(args, format);
+  log_callback_.Run("PAOS::" + base::StringPrintV(format, args) +
+                    base::StringPrintf(" [this=%p]", this));
+  va_end(args);
+}
+
 void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
   int bytes_remaining = requested_bytes;
   while (bytes_remaining > 0) {
@@ -231,7 +223,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   CHECK(callback);
   CHECK(pa_stream_);
-  SendLogMessage(log_callback_, "Start([this=%p])", this);
+  SendLogMessage("%s()", __func__);
 
   AutoPulseLock auto_lock(pa_mainloop_);
 
@@ -255,7 +247,7 @@
 
 void PulseAudioOutputStream::Stop() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  SendLogMessage(log_callback_, "Stop([this=%p])", this);
+  SendLogMessage("%s()", __func__);
 
   // Cork (pause) the stream.  Waiting for the main loop lock will ensure
   // outstanding callbacks have completed.
diff --git a/media/audio/pulse/pulse_output.h b/media/audio/pulse/pulse_output.h
index 59ce6168..6a85b26 100644
--- a/media/audio/pulse/pulse_output.h
+++ b/media/audio/pulse/pulse_output.h
@@ -57,6 +57,9 @@
   void GetVolume(double* volume) override;
 
  private:
+  // Helper method used for sending native logs to the registered client.
+  void SendLogMessage(const char* format, ...) PRINTF_FORMAT(2, 3);
+
   // Called by PulseAudio when |pa_stream_| change state.  If an unexpected
   // failure state change happens and |source_callback_| is set
   // this method will forward the error via OnError().
diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc
index b8da03b9..5282739 100644
--- a/media/audio/win/audio_low_latency_output_win.cc
+++ b/media/audio/win/audio_low_latency_output_win.cc
@@ -6,6 +6,7 @@
 
 #include <Functiondiscoverykeys_devpkey.h>
 #include <audiopolicy.h>
+#include <inttypes.h>
 #include <objbase.h>
 
 #include <climits>
@@ -455,7 +456,9 @@
     return;
   va_list args;
   va_start(args, format);
-  log_callback_.Run("WAOS::" + base::StringPrintV(format, args));
+  log_callback_.Run("WAOS::" + base::StringPrintV(format, args) +
+                    base::StringPrintf(" [this=0x%" PRIXPTR "]",
+                                       reinterpret_cast<uintptr_t>(this)));
   va_end(args);
 }
 
diff --git a/media/gpu/android/codec_image.cc b/media/gpu/android/codec_image.cc
index d57cea86..187fb24 100644
--- a/media/gpu/android/codec_image.cc
+++ b/media/gpu/android/codec_image.cc
@@ -234,9 +234,8 @@
   return !!texture_owner();
 }
 
-gpu::gles2::Texture* CodecImage::GetTexture() const {
-  DCHECK(texture_owner());
-  return gpu::gles2::Texture::CheckedCast(texture_owner()->GetTextureBase());
+gpu::TextureBase* CodecImage::GetTextureBase() const {
+  return texture_owner()->GetTextureBase();
 }
 
 bool CodecImage::RenderToFrontBuffer() {
diff --git a/media/gpu/android/codec_image.h b/media/gpu/android/codec_image.h
index fe4ed50c..6186182 100644
--- a/media/gpu/android/codec_image.h
+++ b/media/gpu/android/codec_image.h
@@ -115,7 +115,7 @@
   bool IsUsingGpuMemory() const override;
   void UpdateAndBindTexImage() override;
   bool HasTextureOwner() const override;
-  gpu::gles2::Texture* GetTexture() const override;
+  gpu::TextureBase* GetTextureBase() const override;
   void NotifyOverlayPromotion(bool promotion, const gfx::Rect& bounds) override;
   // Renders this image to the overlay. Returns true if the buffer is in the
   // overlay front buffer. Returns false if the buffer was invalidated.
diff --git a/net/base/features.cc b/net/base/features.cc
index 196a509f..2e41e84 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -101,8 +101,7 @@
 #endif
 
 const base::Feature kAppendFrameOriginToNetworkIsolationKey{
-    "AppendFrameOriginToNetworkIsolationKey",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    "AppendFrameOriginToNetworkIsolationKey", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kUseRegistrableDomainInNetworkIsolationKey{
     "UseRegistrableDomainInNetworkIsolationKey",
diff --git a/net/base/network_change_notifier_win.cc b/net/base/network_change_notifier_win.cc
index c453bda..099ead373 100644
--- a/net/base/network_change_notifier_win.cc
+++ b/net/base/network_change_notifier_win.cc
@@ -273,8 +273,8 @@
   // that interval.
   if (sequential_failures_ > 0) {
     RecomputeCurrentConnectionTypeOnBlockingSequence(
-        base::Bind(&NetworkChangeNotifierWin::NotifyObservers,
-                   weak_factory_.GetWeakPtr()));
+        base::BindOnce(&NetworkChangeNotifierWin::NotifyObservers,
+                       weak_factory_.GetWeakPtr()));
   }
 
   if (sequential_failures_ < 2000) {
diff --git a/net/base/network_isolation_key_unittest.cc b/net/base/network_isolation_key_unittest.cc
index 842822e..a595bed 100644
--- a/net/base/network_isolation_key_unittest.cc
+++ b/net/base/network_isolation_key_unittest.cc
@@ -152,33 +152,28 @@
   const url::Origin kJunkOrigin =
       url::Origin::Create(GURL("data:text/html,junk"));
 
-  // Convert empty key to value and back, expecting the same value.
-  NetworkIsolationKey no_frame_origin_key;
-  base::Value no_frame_origin_value;
-  ASSERT_TRUE(no_frame_origin_key.ToValue(&no_frame_origin_value));
+  for (bool use_frame_origins : {true, false}) {
+    SCOPED_TRACE(use_frame_origins);
+    base::test::ScopedFeatureList feature_list;
+    if (use_frame_origins) {
+      feature_list.InitAndEnableFeature(
+          features::kAppendFrameOriginToNetworkIsolationKey);
+    } else {
+      feature_list.InitAndDisableFeature(
+          features::kAppendFrameOriginToNetworkIsolationKey);
+    }
 
-  // Fill initial value with junk data, to make sure it's overwritten.
-  NetworkIsolationKey out_key(kJunkOrigin, kJunkOrigin);
-  EXPECT_TRUE(NetworkIsolationKey::FromValue(no_frame_origin_value, &out_key));
-  EXPECT_EQ(no_frame_origin_key, out_key);
+    // Convert empty key to value and back, expecting the same value.
+    NetworkIsolationKey no_frame_origin_key;
+    base::Value no_frame_origin_value;
+    ASSERT_TRUE(no_frame_origin_key.ToValue(&no_frame_origin_value));
 
-  // Perform same checks when frame origins are enabled.
-
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(
-      features::kAppendFrameOriginToNetworkIsolationKey);
-
-  NetworkIsolationKey frame_origin_key;
-  base::Value frame_origin_value;
-  ASSERT_TRUE(frame_origin_key.ToValue(&frame_origin_value));
-
-  // Fill initial value with junk data, to make sure it's overwritten.
-  out_key = NetworkIsolationKey(kJunkOrigin, kJunkOrigin);
-  EXPECT_TRUE(NetworkIsolationKey::FromValue(frame_origin_value, &out_key));
-  EXPECT_EQ(frame_origin_key, out_key);
-
-  // The Values should also be the same in both cases.
-  EXPECT_EQ(no_frame_origin_key, frame_origin_key);
+    // Fill initial value with junk data, to make sure it's overwritten.
+    NetworkIsolationKey out_key(kJunkOrigin, kJunkOrigin);
+    EXPECT_TRUE(
+        NetworkIsolationKey::FromValue(no_frame_origin_value, &out_key));
+    EXPECT_EQ(no_frame_origin_key, out_key);
+  }
 }
 
 TEST(NetworkIsolationKeyTest, ValueRoundTripNoFrameOrigin) {
@@ -236,12 +231,15 @@
   const url::Origin kTransientOrigin =
       url::Origin::Create(GURL("data:text/html,transient"));
 
-  for (bool use_frame_origins : {false, true}) {
+  for (bool use_frame_origins : {true, false}) {
     SCOPED_TRACE(use_frame_origins);
     base::test::ScopedFeatureList feature_list;
     if (use_frame_origins) {
       feature_list.InitAndEnableFeature(
           features::kAppendFrameOriginToNetworkIsolationKey);
+    } else {
+      feature_list.InitAndDisableFeature(
+          features::kAppendFrameOriginToNetworkIsolationKey);
     }
 
     NetworkIsolationKey key1(kTransientOrigin, kTransientOrigin);
@@ -273,12 +271,15 @@
       base::Value(std::move(too_many_origins_list)),
   };
 
-  for (bool use_frame_origins : {false, true}) {
+  for (bool use_frame_origins : {true, false}) {
     SCOPED_TRACE(use_frame_origins);
     base::test::ScopedFeatureList feature_list;
     if (use_frame_origins) {
       feature_list.InitAndEnableFeature(
           features::kAppendFrameOriginToNetworkIsolationKey);
+    } else {
+      feature_list.InitAndDisableFeature(
+          features::kAppendFrameOriginToNetworkIsolationKey);
     }
 
     for (const auto& test_case : kTestCases) {
@@ -495,8 +496,11 @@
 // host when using a non-standard scheme.
 TEST(NetworkIsolationKeyTest, UseRegistrableDomainWithNonStandardScheme) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(
-      features::kUseRegistrableDomainInNetworkIsolationKey);
+  feature_list.InitWithFeatures(
+      // enabled_features
+      {features::kUseRegistrableDomainInNetworkIsolationKey},
+      // disabled_features
+      {features::kAppendFrameOriginToNetworkIsolationKey});
 
   // Have to register the scheme, or url::Origin::Create() will return an opaque
   // origin.
@@ -528,9 +532,10 @@
 }
 
 TEST(NetworkIsolationKeyTest, CreateTransient) {
-  for (bool append_frame_origin : {false, true}) {
+  for (bool use_frame_origins : {true, false}) {
+    SCOPED_TRACE(use_frame_origins);
     base::test::ScopedFeatureList feature_list;
-    if (append_frame_origin) {
+    if (use_frame_origins) {
       feature_list.InitAndEnableFeature(
           features::kAppendFrameOriginToNetworkIsolationKey);
     } else {
diff --git a/net/disk_cache/simple/simple_test_util.h b/net/disk_cache/simple/simple_test_util.h
index 79a6468..36af9285 100644
--- a/net/disk_cache/simple/simple_test_util.h
+++ b/net/disk_cache/simple/simple_test_util.h
@@ -24,7 +24,7 @@
  public:
   static const size_t size = Size;
 
-  ImmutableArray(const base::Callback<T (size_t index)>& initializer) {
+  ImmutableArray(const base::RepeatingCallback<T(size_t index)>& initializer) {
     for (size_t i = 0; i < size; ++i)
       data_[i] = initializer.Run(i);
   }
diff --git a/net/http/structured_headers_unittest.cc b/net/http/structured_headers_unittest.cc
index e593865..afb77ab 100644
--- a/net/http/structured_headers_unittest.cc
+++ b/net/http/structured_headers_unittest.cc
@@ -620,8 +620,50 @@
      "a=3, b=2"},
     {"numeric key dictionary", "a=1,1b=2,a=1", base::nullopt},
     {"uppercase key dictionary", "a=1,B=2,a=1", base::nullopt},
-    {"bad key dictionary", "a=1,b!=2,a=1", base::nullopt}};
-
+    {"bad key dictionary", "a=1,b!=2,a=1", base::nullopt},
+    // Paramaterised dictionary tests
+    {"basic parameterised dict",
+     "abc=123;a=1;b=2, def=456, ghi=789;q=9;r=\"+w\"",
+     {Dictionary{
+         {{"abc", {Integer(123), {Param("a", 1), Param("b", 2)}}},
+          {"def", {Integer(456), {}}},
+          {"ghi", {Integer(789), {Param("q", 9), Param("r", "+w")}}}}}}},
+    {"single item parameterised dict",
+     "a=b; q=1.0",
+     {Dictionary{{{"a", {Token("b"), {DoubleParam("q", 1.0)}}}}}},
+     "a=b;q=1.0"},
+    {"list item parameterised dictionary",
+     "a=(1 2); q=1.0",
+     {Dictionary{{{"a",
+                   {{{Integer(1L), {}}, {Integer(2L), {}}},
+                    {DoubleParam("q", 1.0)}}}}}},
+     "a=(1 2);q=1.0"},
+    {"missing parameter value parameterised dict",
+     "a=3;c;d=5",
+     {Dictionary{{{"a", {Integer(3), {Param("c"), Param("d", 5)}}}}}}},
+    {"terminal missing parameter value parameterised dict",
+     "a=3;c=5;d",
+     {Dictionary{{{"a", {Integer(3), {Param("c", 5), Param("d")}}}}}}},
+    {"no whitespace parameterised dict",
+     "a=b;c=1,d=e;f=2",
+     {Dictionary{{{"a", {Token("b"), {Param("c", 1)}}},
+                  {"d", {Token("e"), {Param("f", 2)}}}}}},
+     "a=b;c=1, d=e;f=2"},
+    {"whitespace before = parameterised dict", "a=b;q =0.5", base::nullopt},
+    {"whitespace after = parameterised dict", "a=b;q= 0.5", base::nullopt},
+    {"whitespace before ; parameterised dict", "a=b ;q=0.5", base::nullopt},
+    {"whitespace after ; parameterised dict",
+     "a=b; q=0.5",
+     {Dictionary{{{"a", {Token("b"), {DoubleParam("q", 0.5)}}}}}},
+     "a=b;q=0.5"},
+    {"extra whitespace parameterised dict",
+     "a=b;  c=1  ,  d=e; f=2; g=3",
+     {Dictionary{{{"a", {Token("b"), {Param("c", 1)}}},
+                  {"d", {Token("e"), {Param("f", 2), Param("g", 3)}}}}}},
+     "a=b;c=1, d=e;f=2;g=3"},
+    {"trailing comma parameterised list", "a=b; q=1.0,", base::nullopt},
+    {"empty item parameterised list", "a=b; q=1.0,,c=d", base::nullopt},
+};
 }  // namespace
 
 TEST(StructuredHeaderTest, ParseBareItem) {
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index 6a4a8b45..df2bd22 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -196,16 +196,18 @@
       // (i.e., base::ThreadTaskRunnerHandle::Get()).
       // Use WeakPtr() to avoid crashes where the socket watcher is destroyed
       // after |this| is destroyed.
-      base::Bind(&NetworkQualityEstimator::OnUpdatedTransportRTTAvailable,
-                 weak_ptr_factory_.GetWeakPtr()),
+      base::BindRepeating(
+          &NetworkQualityEstimator::OnUpdatedTransportRTTAvailable,
+          weak_ptr_factory_.GetWeakPtr()),
       // ShouldSocketWatcherNotifyRTT() below is called by only the socket
       // watchers that live on the same thread as the current thread
       // (i.e., base::ThreadTaskRunnerHandle::Get()). Also, network quality
       // estimator is destroyed after network contexts and URLRequestContexts.
       // It's safe to use base::Unretained() below since the socket watcher
       // (owned by sockets) would be destroyed before |this|.
-      base::Bind(&NetworkQualityEstimator::ShouldSocketWatcherNotifyRTT,
-                 base::Unretained(this)),
+      base::BindRepeating(
+          &NetworkQualityEstimator::ShouldSocketWatcherNotifyRTT,
+          base::Unretained(this)),
       tick_clock_));
 
   GatherEstimatesForNextConnectionType();
diff --git a/net/nqe/socket_watcher.h b/net/nqe/socket_watcher.h
index a4bf0415..7fe97c9 100644
--- a/net/nqe/socket_watcher.h
+++ b/net/nqe/socket_watcher.h
@@ -30,12 +30,13 @@
 
 namespace {
 
-typedef base::Callback<void(SocketPerformanceWatcherFactory::Protocol protocol,
-                            const base::TimeDelta& rtt,
-                            const base::Optional<nqe::internal::IPHash>& host)>
+typedef base::RepeatingCallback<void(
+    SocketPerformanceWatcherFactory::Protocol protocol,
+    const base::TimeDelta& rtt,
+    const base::Optional<nqe::internal::IPHash>& host)>
     OnUpdatedRTTAvailableCallback;
 
-typedef base::Callback<bool(base::TimeTicks)> ShouldNotifyRTTCallback;
+typedef base::RepeatingCallback<bool(base::TimeTicks)> ShouldNotifyRTTCallback;
 
 }  // namespace
 
diff --git a/net/nqe/socket_watcher_factory.h b/net/nqe/socket_watcher_factory.h
index 6753f3a..769a7b12 100644
--- a/net/nqe/socket_watcher_factory.h
+++ b/net/nqe/socket_watcher_factory.h
@@ -27,12 +27,13 @@
 
 namespace {
 
-typedef base::Callback<void(SocketPerformanceWatcherFactory::Protocol protocol,
-                            const base::TimeDelta& rtt,
-                            const base::Optional<nqe::internal::IPHash>& host)>
+typedef base::RepeatingCallback<void(
+    SocketPerformanceWatcherFactory::Protocol protocol,
+    const base::TimeDelta& rtt,
+    const base::Optional<nqe::internal::IPHash>& host)>
     OnUpdatedRTTAvailableCallback;
 
-typedef base::Callback<bool(base::TimeTicks)> ShouldNotifyRTTCallback;
+typedef base::RepeatingCallback<bool(base::TimeTicks)> ShouldNotifyRTTCallback;
 
 }  // namespace
 
diff --git a/net/nqe/socket_watcher_unittest.cc b/net/nqe/socket_watcher_unittest.cc
index 8831ae1..b3d592f2 100644
--- a/net/nqe/socket_watcher_unittest.cc
+++ b/net/nqe/socket_watcher_unittest.cc
@@ -114,8 +114,9 @@
   SocketWatcher socket_watcher(
       SocketPerformanceWatcherFactory::PROTOCOL_TCP, address_list,
       base::TimeDelta::FromMilliseconds(2000), false,
-      base::ThreadTaskRunnerHandle::Get(), base::Bind(OnUpdatedRTTAvailable),
-      base::Bind(ShouldNotifyRTTCallback), &tick_clock);
+      base::ThreadTaskRunnerHandle::Get(),
+      base::BindRepeating(OnUpdatedRTTAvailable),
+      base::BindRepeating(ShouldNotifyRTTCallback), &tick_clock);
 
   EXPECT_TRUE(socket_watcher.ShouldNotifyUpdatedRTT());
   socket_watcher.OnUpdatedRTTAvailable(base::TimeDelta::FromSeconds(10));
@@ -158,8 +159,8 @@
       SocketPerformanceWatcherFactory::PROTOCOL_QUIC, address_list,
       base::TimeDelta::FromMilliseconds(2000), false,
       base::ThreadTaskRunnerHandle::Get(),
-      base::Bind(OnUpdatedRTTAvailableStoreParams),
-      base::Bind(ShouldNotifyRTTCallback), &tick_clock);
+      base::BindRepeating(OnUpdatedRTTAvailableStoreParams),
+      base::BindRepeating(ShouldNotifyRTTCallback), &tick_clock);
 
   EXPECT_TRUE(socket_watcher.ShouldNotifyUpdatedRTT());
   socket_watcher.OnUpdatedRTTAvailable(base::TimeDelta::FromSeconds(10));
@@ -212,8 +213,9 @@
     SocketWatcher socket_watcher(
         SocketPerformanceWatcherFactory::PROTOCOL_TCP, address_list,
         base::TimeDelta::FromMilliseconds(2000), false,
-        base::ThreadTaskRunnerHandle::Get(), base::Bind(OnUpdatedRTTAvailable),
-        base::Bind(ShouldNotifyRTTCallback), &tick_clock);
+        base::ThreadTaskRunnerHandle::Get(),
+        base::BindRepeating(OnUpdatedRTTAvailable),
+        base::BindRepeating(ShouldNotifyRTTCallback), &tick_clock);
 
     EXPECT_EQ(test.expect_should_notify_rtt,
               socket_watcher.ShouldNotifyUpdatedRTT());
@@ -251,8 +253,8 @@
         SocketPerformanceWatcherFactory::PROTOCOL_TCP, address_list,
         base::TimeDelta::FromMilliseconds(2000), false,
         base::ThreadTaskRunnerHandle::Get(),
-        base::Bind(OnUpdatedRTTAvailableStoreParams),
-        base::Bind(ShouldNotifyRTTCallback), &tick_clock);
+        base::BindRepeating(OnUpdatedRTTAvailableStoreParams),
+        base::BindRepeating(ShouldNotifyRTTCallback), &tick_clock);
     EXPECT_TRUE(socket_watcher.ShouldNotifyUpdatedRTT());
     socket_watcher.OnUpdatedRTTAvailable(base::TimeDelta::FromSeconds(10));
     base::RunLoop().RunUntilIdle();
diff --git a/net/socket/tcp_socket_win.cc b/net/socket/tcp_socket_win.cc
index 4b67446..dbc2aa96 100644
--- a/net/socket/tcp_socket_win.cc
+++ b/net/socket/tcp_socket_win.cc
@@ -489,9 +489,9 @@
   DCHECK(!core_->read_iobuffer_.get());
   // base::Unretained() is safe because RetryRead() won't be called when |this|
   // is gone.
-  int rv =
-      ReadIfReady(buf, buf_len,
-                  base::Bind(&TCPSocketWin::RetryRead, base::Unretained(this)));
+  int rv = ReadIfReady(
+      buf, buf_len,
+      base::BindOnce(&TCPSocketWin::RetryRead, base::Unretained(this)));
   if (rv != ERR_IO_PENDING)
     return rv;
   read_callback_ = std::move(callback);
@@ -932,7 +932,7 @@
     // |this| is gone.
     rv = ReadIfReady(
         core_->read_iobuffer_.get(), core_->read_buffer_length_,
-        base::Bind(&TCPSocketWin::RetryRead, base::Unretained(this)));
+        base::BindOnce(&TCPSocketWin::RetryRead, base::Unretained(this)));
     if (rv == ERR_IO_PENDING)
       return;
   }
diff --git a/net/socket/transport_client_socket_pool_test_util.cc b/net/socket/transport_client_socket_pool_test_util.cc
index ba52c32..1815967 100644
--- a/net/socket/transport_client_socket_pool_test_util.cc
+++ b/net/socket/transport_client_socket_pool_test_util.cc
@@ -199,9 +199,9 @@
   // Call this method to get a closure which will trigger the connect callback
   // when called. The closure can be called even after the socket is deleted; it
   // will safely do nothing.
-  base::Closure GetConnectCallback() {
-    return base::Bind(&MockTriggerableClientSocket::DoCallback,
-                      weak_factory_.GetWeakPtr());
+  base::OnceClosure GetConnectCallback() {
+    return base::BindOnce(&MockTriggerableClientSocket::DoCallback,
+                          weak_factory_.GetWeakPtr());
   }
 
   static std::unique_ptr<TransportClientSocket> MakeMockPendingClientSocket(
@@ -432,7 +432,7 @@
       // don't need to worry about atomicity because this code is
       // single-threaded.
       if (!run_loop_quit_closure_.is_null())
-        run_loop_quit_closure_.Run();
+        std::move(run_loop_quit_closure_).Run();
       return std::move(rv);
     }
     default:
@@ -476,7 +476,7 @@
   client_socket_index_max_ = num_types;
 }
 
-base::Closure
+base::OnceClosure
 MockTransportClientSocketFactory::WaitForTriggerableSocketCreation() {
   while (triggerable_sockets_.empty()) {
     base::RunLoop run_loop;
@@ -484,7 +484,7 @@
     run_loop.Run();
     run_loop_quit_closure_.Reset();
   }
-  base::Closure trigger = triggerable_sockets_.front();
+  base::OnceClosure trigger = std::move(triggerable_sockets_.front());
   triggerable_sockets_.pop();
   return trigger;
 }
diff --git a/net/socket/transport_client_socket_pool_test_util.h b/net/socket/transport_client_socket_pool_test_util.h
index a10aea1..e545f7d 100644
--- a/net/socket/transport_client_socket_pool_test_util.h
+++ b/net/socket/transport_client_socket_pool_test_util.h
@@ -122,7 +122,7 @@
   // have been created yet, wait for one to be created before returning the
   // Closure. This method should be called the same number of times as
   // MOCK_TRIGGERABLE_CLIENT_SOCKETs are created in the test.
-  base::Closure WaitForTriggerableSocketCreation();
+  base::OnceClosure WaitForTriggerableSocketCreation();
 
  private:
   NetLog* net_log_;
@@ -132,8 +132,8 @@
   int client_socket_index_;
   int client_socket_index_max_;
   base::TimeDelta delay_;
-  base::queue<base::Closure> triggerable_sockets_;
-  base::Closure run_loop_quit_closure_;
+  base::queue<base::OnceClosure> triggerable_sockets_;
+  base::OnceClosure run_loop_quit_closure_;
 
   DISALLOW_COPY_AND_ASSIGN(MockTransportClientSocketFactory);
 };
diff --git a/net/socket/websocket_transport_client_socket_pool_unittest.cc b/net/socket/websocket_transport_client_socket_pool_unittest.cc
index 24ed21d..14661598 100644
--- a/net/socket/websocket_transport_client_socket_pool_unittest.cc
+++ b/net/socket/websocket_transport_client_socket_pool_unittest.cc
@@ -58,9 +58,9 @@
 // RunLoop doesn't support this natively but it is easy to emulate.
 void RunLoopForTimePeriod(base::TimeDelta period) {
   base::RunLoop run_loop;
-  base::Closure quit_closure(run_loop.QuitClosure());
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, quit_closure,
-                                                       period);
+  base::OnceClosure quit_closure(run_loop.QuitClosure());
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, std::move(quit_closure), period);
   run_loop.Run();
 }
 
@@ -795,13 +795,13 @@
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   ASSERT_FALSE(handle.socket());
 
-  base::Closure ipv6_connect_trigger =
+  base::OnceClosure ipv6_connect_trigger =
       client_socket_factory_.WaitForTriggerableSocketCreation();
-  base::Closure ipv4_connect_trigger =
+  base::OnceClosure ipv4_connect_trigger =
       client_socket_factory_.WaitForTriggerableSocketCreation();
 
-  ipv4_connect_trigger.Run();
-  ipv6_connect_trigger.Run();
+  std::move(ipv4_connect_trigger).Run();
+  std::move(ipv6_connect_trigger).Run();
 
   EXPECT_THAT(callback.WaitForResult(), IsOk());
   ASSERT_TRUE(handle.socket());
@@ -1096,10 +1096,10 @@
 
   EXPECT_THAT(StartRequest(kDefaultPriority), IsError(ERR_IO_PENDING));
 
-  base::Closure connect_trigger =
+  base::OnceClosure connect_trigger =
       client_socket_factory_.WaitForTriggerableSocketCreation();
 
-  connect_trigger.Run();  // Calls InvokeUserCallbackLater()
+  std::move(connect_trigger).Run();  // Calls InvokeUserCallbackLater()
 
   request(0)->handle()->Reset();  // calls CancelRequest()
 
diff --git a/net/spdy/spdy_stream_unittest.cc b/net/spdy/spdy_stream_unittest.cc
index 4f6c5ff..0f56bcd 100644
--- a/net/spdy/spdy_stream_unittest.cc
+++ b/net/spdy/spdy_stream_unittest.cc
@@ -66,7 +66,7 @@
  protected:
   // A function that takes a SpdyStream and the number of bytes which
   // will unstall the next frame completely.
-  typedef base::Callback<void(const base::WeakPtr<SpdyStream>&, int32_t)>
+  typedef base::OnceCallback<void(const base::WeakPtr<SpdyStream>&, int32_t)>
       UnstallFunction;
 
   SpdyStreamTest()
@@ -88,10 +88,9 @@
   void TearDown() override { base::RunLoop().RunUntilIdle(); }
 
   void RunResumeAfterUnstallRequestResponseTest(
-      const UnstallFunction& unstall_function);
+      UnstallFunction unstall_function);
 
-  void RunResumeAfterUnstallBidirectionalTest(
-      const UnstallFunction& unstall_function);
+  void RunResumeAfterUnstallBidirectionalTest(UnstallFunction unstall_function);
 
   // Add{Read,Write}() populates lists that are eventually passed to a
   // SocketData class. |frame| must live for the whole test.
@@ -1327,7 +1326,7 @@
 // request/response (i.e., an HTTP-like) stream resumes after a stall
 // and unstall.
 void SpdyStreamTest::RunResumeAfterUnstallRequestResponseTest(
-    const UnstallFunction& unstall_function) {
+    UnstallFunction unstall_function) {
   spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
       kDefaultUrl, 1, kPostBodyLength, LOWEST, nullptr, 0));
   AddWrite(req);
@@ -1372,7 +1371,7 @@
 
   EXPECT_TRUE(stream->send_stalled_by_flow_control());
 
-  unstall_function.Run(stream, kPostBodyLength);
+  std::move(unstall_function).Run(stream, kPostBodyLength);
 
   EXPECT_FALSE(stream->send_stalled_by_flow_control());
 
@@ -1386,18 +1385,18 @@
 
 TEST_F(SpdyStreamTest, ResumeAfterSendWindowSizeIncreaseRequestResponse) {
   RunResumeAfterUnstallRequestResponseTest(
-      base::Bind(&IncreaseStreamSendWindowSize));
+      base::BindOnce(&IncreaseStreamSendWindowSize));
 }
 
 TEST_F(SpdyStreamTest, ResumeAfterSendWindowSizeAdjustRequestResponse) {
   RunResumeAfterUnstallRequestResponseTest(
-      base::Bind(&AdjustStreamSendWindowSize));
+      base::BindOnce(&AdjustStreamSendWindowSize));
 }
 
 // Given an unstall function, runs a test to make sure that a bidirectional
 // (i.e., non-HTTP-like) stream resumes after a stall and unstall.
 void SpdyStreamTest::RunResumeAfterUnstallBidirectionalTest(
-    const UnstallFunction& unstall_function) {
+    UnstallFunction unstall_function) {
   spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
       kDefaultUrl, 1, kPostBodyLength, LOWEST, nullptr, 0));
   AddWrite(req);
@@ -1451,7 +1450,7 @@
 
   EXPECT_TRUE(stream->send_stalled_by_flow_control());
 
-  unstall_function.Run(stream, kPostBodyLength);
+  std::move(unstall_function).Run(stream, kPostBodyLength);
 
   EXPECT_FALSE(stream->send_stalled_by_flow_control());
 
@@ -1466,12 +1465,12 @@
 
 TEST_F(SpdyStreamTest, ResumeAfterSendWindowSizeIncreaseBidirectional) {
   RunResumeAfterUnstallBidirectionalTest(
-      base::Bind(&IncreaseStreamSendWindowSize));
+      base::BindOnce(&IncreaseStreamSendWindowSize));
 }
 
 TEST_F(SpdyStreamTest, ResumeAfterSendWindowSizeAdjustBidirectional) {
   RunResumeAfterUnstallBidirectionalTest(
-      base::Bind(&AdjustStreamSendWindowSize));
+      base::BindOnce(&AdjustStreamSendWindowSize));
 }
 
 // Test calculation of amount of bytes received from network.
diff --git a/net/test/embedded_test_server/android/embedded_test_server_android.cc b/net/test/embedded_test_server/android/embedded_test_server_android.cc
index 9c2ce0b7..c29ee54 100644
--- a/net/test/embedded_test_server/android/embedded_test_server_android.cc
+++ b/net/test/embedded_test_server/android/embedded_test_server_android.cc
@@ -120,7 +120,7 @@
     const JavaParamRef<jobject>& jobj,
     jlong handler) {
   HandleRequestPtr handler_ptr = reinterpret_cast<HandleRequestPtr>(handler);
-  test_server_.RegisterRequestHandler(base::Bind(handler_ptr));
+  test_server_.RegisterRequestHandler(base::BindRepeating(handler_ptr));
 }
 
 void EmbeddedTestServerAndroid::ServeFilesFromDirectory(
diff --git a/net/test/embedded_test_server/embedded_test_server.cc b/net/test/embedded_test_server/embedded_test_server.cc
index f847841..01e94cd 100644
--- a/net/test/embedded_test_server/embedded_test_server.cc
+++ b/net/test/embedded_test_server/embedded_test_server.cc
@@ -495,7 +495,7 @@
 
 void EmbeddedTestServer::ServeFilesFromDirectory(
     const base::FilePath& directory) {
-  RegisterDefaultHandler(base::Bind(&HandleFileRequest, directory));
+  RegisterDefaultHandler(base::BindRepeating(&HandleFileRequest, directory));
 }
 
 void EmbeddedTestServer::ServeFilesFromSourceDirectory(
@@ -588,8 +588,9 @@
 
   std::unique_ptr<HttpConnection> http_connection_ptr =
       std::make_unique<HttpConnection>(
-          std::move(socket), base::Bind(&EmbeddedTestServer::HandleRequest,
-                                        base::Unretained(this)));
+          std::move(socket),
+          base::BindRepeating(&EmbeddedTestServer::HandleRequest,
+                              base::Unretained(this)));
   HttpConnection* http_connection = http_connection_ptr.get();
   connections_[http_connection->socket_.get()] = std::move(http_connection_ptr);
 
diff --git a/net/test/embedded_test_server/embedded_test_server.h b/net/test/embedded_test_server/embedded_test_server.h
index 0807e3a3..617b7b8 100644
--- a/net/test/embedded_test_server/embedded_test_server.h
+++ b/net/test/embedded_test_server/embedded_test_server.h
@@ -53,7 +53,7 @@
 // void SetUp() {
 //   test_server_ = std::make_unique<EmbeddedTestServer>();
 //   test_server_->RegisterRequestHandler(
-//       base::Bind(&FooTest::HandleRequest, base::Unretained(this)));
+//       base::BindRepeating(&FooTest::HandleRequest, base::Unretained(this)));
 //   ASSERT_TRUE((test_server_handle_ = test_server_.StartAndReturnHandle()));
 // }
 //
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc
index ecce8f01..d599f38 100644
--- a/net/url_request/url_request_http_job_unittest.cc
+++ b/net/url_request/url_request_http_job_unittest.cc
@@ -1381,7 +1381,7 @@
     std::unique_ptr<URLRequest> r(context.CreateRequest(
         url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
     r->SetExtraRequestHeaders(extra_headers);
-    r->SetRequestHeadersCallback(base::Bind(
+    r->SetRequestHeadersCallback(base::BindRepeating(
         &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers)));
 
     r->Start();
@@ -1411,7 +1411,7 @@
 
     std::unique_ptr<URLRequest> r(context.CreateRequest(
         url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
-    r->SetRequestHeadersCallback(base::Bind(
+    r->SetRequestHeadersCallback(base::BindRepeating(
         &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers)));
 
     r->Start();
@@ -1430,7 +1430,7 @@
 
     std::unique_ptr<URLRequest> r(context.CreateRequest(
         url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
-    r->SetRequestHeadersCallback(base::Bind(
+    r->SetRequestHeadersCallback(base::BindRepeating(
         &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers)));
 
     r->Start();
diff --git a/net/url_request/url_request_quic_perftest.cc b/net/url_request/url_request_quic_perftest.cc
index 8306d8cd..649f6b3 100644
--- a/net/url_request/url_request_quic_perftest.cc
+++ b/net/url_request/url_request_quic_perftest.cc
@@ -240,7 +240,7 @@
       base::trace_event::MemoryDumpLevelOfDetail::LIGHT};
 
   auto on_memory_dump_done =
-      [](base::Closure quit_closure, const URLRequestContext* context,
+      [](base::OnceClosure quit_closure, const URLRequestContext* context,
          bool success, uint64_t dump_guid,
          std::unique_ptr<base::trace_event::ProcessMemoryDump> pmd) {
         ASSERT_TRUE(success);
@@ -275,7 +275,7 @@
             reinterpret_cast<uintptr_t>(
                 context->http_transaction_factory()->GetSession()));
         ASSERT_EQ(0u, allocator_dumps.count(stream_factory_dump_name));
-        quit_closure.Run();
+        std::move(quit_closure).Run();
       };
   base::trace_event::MemoryDumpManager::GetInstance()->CreateProcessDump(
       args,
diff --git a/net/websockets/websocket_deflate_stream_test.cc b/net/websockets/websocket_deflate_stream_test.cc
index 078a96cf..4f803427 100644
--- a/net/websockets/websocket_deflate_stream_test.cc
+++ b/net/websockets/websocket_deflate_stream_test.cc
@@ -410,7 +410,7 @@
   ReadFramesStub stub(ERR_IO_PENDING);
   std::vector<std::unique_ptr<WebSocketFrame>> frames;
   base::MockCallback<CompletionOnceCallback> mock_callback;
-  base::MockCallback<base::Closure> checkpoint;
+  base::MockCallback<base::OnceClosure> checkpoint;
 
   {
     InSequence s;
@@ -441,7 +441,7 @@
   ReadFramesStub stub(ERR_IO_PENDING);
   std::vector<std::unique_ptr<WebSocketFrame>> frames;
   base::MockCallback<CompletionOnceCallback> mock_callback;
-  base::MockCallback<base::Closure> checkpoint;
+  base::MockCallback<base::OnceClosure> checkpoint;
 
   {
     InSequence s;
@@ -490,7 +490,7 @@
   ReadFramesStub stub(ERR_IO_PENDING);
 
   base::MockCallback<CompletionOnceCallback> mock_callback;
-  base::MockCallback<base::Closure> checkpoint;
+  base::MockCallback<base::OnceClosure> checkpoint;
   std::vector<std::unique_ptr<WebSocketFrame>> frames;
   {
     InSequence s;
@@ -528,7 +528,7 @@
            data1);
   ReadFramesStub stub1(OK, &frames_to_output), stub2(ERR_IO_PENDING);
   base::MockCallback<CompletionOnceCallback> mock_callback;
-  base::MockCallback<base::Closure> checkpoint;
+  base::MockCallback<base::OnceClosure> checkpoint;
   std::vector<std::unique_ptr<WebSocketFrame>> frames;
 
   {
@@ -1068,7 +1068,7 @@
 TEST_F(WebSocketDeflateStreamTest, WriteFrameAsync) {
   WriteFramesStub stub(predictor_, ERR_IO_PENDING);
   base::MockCallback<CompletionOnceCallback> mock_callback;
-  base::MockCallback<base::Closure> checkpoint;
+  base::MockCallback<base::OnceClosure> checkpoint;
   std::vector<std::unique_ptr<WebSocketFrame>> frames;
   {
     InSequence s;
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index d9789a7..ff5a1c0 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -405,6 +405,15 @@
   return If(AnyOf(pid == 0, pid == target_pid), Allow()).Else(Error(EPERM));
 }
 
+ResultExpr RestrictPrlimitToGetrlimit(pid_t target_pid) {
+  const Arg<pid_t> pid(0);
+  const Arg<uintptr_t> new_limit(2);
+  // Only allow operations for the current process, and only with |new_limit|
+  // set to null.
+  return If(AllOf(new_limit == 0, AnyOf(pid == 0, pid == target_pid)), Allow())
+      .Else(Error(EPERM));
+}
+
 #if !defined(OS_NACL_NONSFI)
 ResultExpr RestrictPtrace() {
   const Arg<int> request(0);
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
index 1544289..ba4289f 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
@@ -104,6 +104,11 @@
 // gracefully; see crbug.com/160157.
 SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrlimit(pid_t target_pid);
 
+// Restrict |pid| to the calling process (or 0) for prlimit64(), and require the
+// |new_limit_ argument to be null.  This allows only getting limits on the
+// current process. Otherwise fail gracefully.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrlimitToGetrlimit(pid_t target_pid);
+
 // Restrict ptrace() to just read operations that are needed for crash
 // reporting. See https://crbug.com/933418 for details.
 SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPtrace();
diff --git a/services/network/public/cpp/cross_origin_embedder_policy.cc b/services/network/public/cpp/cross_origin_embedder_policy.cc
index 87b1fe1..aa2c3ee7 100644
--- a/services/network/public/cpp/cross_origin_embedder_policy.cc
+++ b/services/network/public/cpp/cross_origin_embedder_policy.cc
@@ -17,5 +17,12 @@
     const CrossOriginEmbedderPolicy& src) = default;
 CrossOriginEmbedderPolicy& CrossOriginEmbedderPolicy::operator=(
     CrossOriginEmbedderPolicy&& src) = default;
+bool CrossOriginEmbedderPolicy::operator==(
+    const CrossOriginEmbedderPolicy& other) const {
+  return value == other.value &&
+         reporting_endpoint == other.reporting_endpoint &&
+         report_only_value == other.report_only_value &&
+         report_only_reporting_endpoint == other.report_only_reporting_endpoint;
+}
 
 }  // namespace network
diff --git a/services/network/public/cpp/cross_origin_embedder_policy.h b/services/network/public/cpp/cross_origin_embedder_policy.h
index a94cb1ea..ea1e12c6 100644
--- a/services/network/public/cpp/cross_origin_embedder_policy.h
+++ b/services/network/public/cpp/cross_origin_embedder_policy.h
@@ -22,6 +22,7 @@
   CrossOriginEmbedderPolicy(CrossOriginEmbedderPolicy&&);
   CrossOriginEmbedderPolicy& operator=(const CrossOriginEmbedderPolicy&);
   CrossOriginEmbedderPolicy& operator=(CrossOriginEmbedderPolicy&&);
+  bool operator==(const CrossOriginEmbedderPolicy&) const;
 
   mojom::CrossOriginEmbedderPolicyValue value =
       mojom::CrossOriginEmbedderPolicyValue::kNone;
diff --git a/services/service_manager/sandbox/linux/bpf_utility_policy_linux.cc b/services/service_manager/sandbox/linux/bpf_utility_policy_linux.cc
index c7f9009..192081e 100644
--- a/services/service_manager/sandbox/linux/bpf_utility_policy_linux.cc
+++ b/services/service_manager/sandbox/linux/bpf_utility_policy_linux.cc
@@ -27,6 +27,9 @@
   switch (sysno) {
     case __NR_ioctl:
       return sandbox::RestrictIoctl();
+    case __NR_prlimit64:
+      // Restrict prlimit() to reference only the calling process.
+      return sandbox::RestrictPrlimitToGetrlimit(GetPolicyPid());
     // Allow the system calls below.
     case __NR_fdatasync:
     case __NR_fsync:
diff --git a/testing/buildbot/chromium.swangle.json b/testing/buildbot/chromium.swangle.json
index b85fe75..f6bdbf0 100644
--- a/testing/buildbot/chromium.swangle.json
+++ b/testing/buildbot/chromium.swangle.json
@@ -48,7 +48,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -73,7 +73,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -98,7 +98,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -153,7 +153,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
@@ -225,7 +225,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -250,7 +250,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -275,7 +275,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -330,7 +330,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
@@ -402,7 +402,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -427,7 +427,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -452,7 +452,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -507,7 +507,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
@@ -579,7 +579,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -604,7 +604,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -629,7 +629,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -684,7 +684,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
@@ -756,7 +756,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -781,7 +781,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -806,7 +806,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -861,7 +861,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
@@ -933,7 +933,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -958,7 +958,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -983,7 +983,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -1038,7 +1038,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
@@ -1110,7 +1110,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -1135,7 +1135,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -1160,7 +1160,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -1215,7 +1215,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
@@ -1287,7 +1287,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -1312,7 +1312,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -1337,7 +1337,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -1392,7 +1392,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
@@ -1464,7 +1464,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -1489,7 +1489,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -1514,7 +1514,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -1569,7 +1569,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
@@ -1641,7 +1641,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -1666,7 +1666,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -1691,7 +1691,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -1746,7 +1746,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
@@ -1818,7 +1818,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -1843,7 +1843,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -1868,7 +1868,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -1923,7 +1923,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
@@ -1995,7 +1995,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 20
+          "shards": 3
         },
         "test": "angle_deqp_gles2_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles2_tests"
@@ -2020,7 +2020,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 6
+          "shards": 4
         },
         "test": "angle_deqp_gles31_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles31_tests"
@@ -2045,7 +2045,7 @@
           ],
           "hard_timeout": 900,
           "io_timeout": 900,
-          "shards": 29
+          "shards": 9
         },
         "test": "angle_deqp_gles3_tests",
         "test_target": "//third_party/angle/src/tests:angle_deqp_gles3_tests"
@@ -2100,7 +2100,7 @@
       },
       {
         "args": [
-          "--gtest_filter=\\*Vulkan_SwiftShader",
+          "--gtest_filter=*Vulkan_SwiftShader",
           "--test-launcher-batch-limit=512",
           "--test-launcher-retry-limit=0"
         ],
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 6f8e6c5..65e9fcd 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3329,7 +3329,7 @@
           '--test-launcher-retry-limit=0',
         ],
         'swarming': {
-          'shards': 20,
+          'shards': 3,
         },
       },
       'angle_deqp_gles31_tests': {
@@ -3339,7 +3339,7 @@
           '--test-launcher-retry-limit=0',
         ],
         'swarming': {
-          'shards': 6,
+          'shards': 4,
         },
       },
       'angle_deqp_gles3_tests': {
@@ -3349,7 +3349,7 @@
           '--test-launcher-retry-limit=0',
         ],
         'swarming': {
-          'shards': 29,
+          'shards': 9,
         },
       },
       'angle_deqp_khr_gles2_tests': {
@@ -3368,7 +3368,7 @@
       },
       'angle_end2end_tests': {
         'args': [
-          '--gtest_filter=\*Vulkan_SwiftShader',
+          '--gtest_filter=*Vulkan_SwiftShader',
           '--test-launcher-batch-limit=512',
           '--test-launcher-retry-limit=0',
         ],
diff --git a/third_party/android_provider/BUILD.gn b/third_party/android_provider/BUILD.gn
new file mode 100644
index 0000000..34bedb42
--- /dev/null
+++ b/third_party/android_provider/BUILD.gn
@@ -0,0 +1,14 @@
+# 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("//build/config/android/rules.gni")
+
+assert(is_android)
+
+android_library("android_provider_java") {
+  sources = [
+    "java/src/org/chromium/third_party/android/provider/MediaStoreUtils.java",
+  ]
+  deps = [ "//third_party/android_deps:androidx_annotation_annotation_java" ]
+}
diff --git a/third_party/android_provider/LICENSE b/third_party/android_provider/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/third_party/android_provider/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/android_provider/OWNERS b/third_party/android_provider/OWNERS
new file mode 100644
index 0000000..f41c891
--- /dev/null
+++ b/third_party/android_provider/OWNERS
@@ -0,0 +1,6 @@
+qinmin@chromium.org
+dtrainor@chromium.org
+
+# TEAM: chrome-downloads@chromium.org
+# COMPONENT: UI>Browser>Downloads
+
diff --git a/third_party/android_provider/README.chromium b/third_party/android_provider/README.chromium
new file mode 100644
index 0000000..56cff5b
--- /dev/null
+++ b/third_party/android_provider/README.chromium
@@ -0,0 +1,17 @@
+Name: MediaStoreUtils Android sample.
+URL: https://android.googlesource.com/platform/cts/+/master/tests/tests/provider/src/android/provider/cts/MediaStoreUtils.java
+Version: 50f25a19f2a3de940d6ef7eac84b37d1c62f1b5f
+License: Apache 2.0
+Security Critical: yes
+
+Description:
+This contains a modified copy of MediaStoreUtils.java. Please don't modify this.
+
+MediaStoreUtils.java is based on a public Android sample that was
+available as part of the Android cts libraries. It is
+also available from:
+https://android.googlesource.com/platform/cts/+/master/tests/tests/provider/src/android/provider/cts/MediaStoreUtils.java
+
+Local Modifications:
+- Added logs
+- Introduced some helper method.
diff --git a/third_party/android_provider/java/src/org/chromium/third_party/android/provider/MediaStoreUtils.java b/third_party/android_provider/java/src/org/chromium/third_party/android/provider/MediaStoreUtils.java
new file mode 100644
index 0000000..c4664aa
--- /dev/null
+++ b/third_party/android_provider/java/src/org/chromium/third_party/android/provider/MediaStoreUtils.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package org.chromium.third_party.android.provider;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.MediaStore;
+import android.provider.MediaStore.DownloadColumns;
+import android.provider.MediaStore.MediaColumns;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.FileNotFoundException;
+import java.io.OutputStream;
+import java.util.Objects;
+
+/**
+ * Utility class to contribute download to the public download collection using
+ * MediaStore API from Q.
+ */
+public class MediaStoreUtils {
+    private static final String TAG = "MediaStoreUtils";
+
+    /**
+     * Creates a new pending media item using the given parameters. Pending items
+     * are expected to have a short lifetime, and owners should either
+     * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
+     * pending item within a few hours after first creating it.
+     *
+     * @param context Application context.
+     * @param params Parameters used to configure the item.
+     * @return token which can be passed to {@link #openPending(Context, Uri)}
+     *         to work with this pending item.
+     */
+    public static @NonNull Uri createPending(
+            @NonNull Context context, @NonNull PendingParams params) {
+        return context.getContentResolver().insert(params.mInsertUri, params.mInsertValues);
+    }
+
+    /**
+     * Opens a pending media item to make progress on it. You can open a pending
+     * item multiple times before finally calling either
+     * {@link PendingSession#publish()} or {@link PendingSession#abandon()}.
+     *
+     * @param uri token which was previously returned from
+     *            {@link #createPending(Context, PendingParams)}.
+     * @return pending session that was opened.
+     */
+    public static @NonNull PendingSession openPending(@NonNull Context context, @NonNull Uri uri) {
+        return new PendingSession(context, uri);
+    }
+
+    /**
+     * Parameters that describe a pending media item.
+     */
+    public static class PendingParams {
+        final Uri mInsertUri;
+        final ContentValues mInsertValues;
+
+        /**
+         * Creates parameters that describe a pending media item.
+         *
+         * @param insertUri the {@code content://} Uri where this pending item
+         *            should be inserted when finally published. For example, to
+         *            publish an image, use
+         *            {@link MediaStore.Images.Media#getContentUri(String)}.
+         * @param displayName Display name of the item.
+         * @param mimeType MIME type of the item.
+         */
+        public PendingParams(
+                @NonNull Uri insertUri, @NonNull String displayName, @NonNull String mimeType) {
+            mInsertUri = Objects.requireNonNull(insertUri);
+            final long now = System.currentTimeMillis() / 1000;
+            mInsertValues = new ContentValues();
+            mInsertValues.put(MediaColumns.DISPLAY_NAME, Objects.requireNonNull(displayName));
+            mInsertValues.put(MediaColumns.MIME_TYPE, Objects.requireNonNull(mimeType));
+            mInsertValues.put(MediaColumns.DATE_ADDED, now);
+            mInsertValues.put(MediaColumns.DATE_MODIFIED, now);
+            try {
+                setPendingContentValues(this.mInsertValues, true);
+            } catch (Exception e) {
+                Log.e(TAG, "Unable to set pending content values.", e);
+            }
+        }
+
+        /**
+         * Optionally sets the Uri from where the file has been downloaded. This is used
+         * for files being added to {@link Downloads} table.
+         *
+         * @see DownloadColumns#DOWNLOAD_URI
+         */
+        public void setDownloadUri(@Nullable Uri downloadUri) {
+            if (downloadUri == null) {
+                mInsertValues.remove(DownloadColumns.DOWNLOAD_URI);
+            } else {
+                mInsertValues.put(DownloadColumns.DOWNLOAD_URI, downloadUri.toString());
+            }
+        }
+
+        /**
+         * Optionally set the Uri indicating HTTP referer of the file. This is used for
+         * files being added to {@link Downloads} table.
+         *
+         * @see DownloadColumns#REFERER_URI
+         */
+        public void setRefererUri(@Nullable Uri refererUri) {
+            if (refererUri == null) {
+                mInsertValues.remove(DownloadColumns.REFERER_URI);
+            } else {
+                mInsertValues.put(DownloadColumns.REFERER_URI, refererUri.toString());
+            }
+        }
+
+        /**
+         * Sets the expiration time of the download.
+         *
+         * @time Epoch time in seconds.
+         */
+        public void setExpirationTime(long time) {
+            mInsertValues.put("date_expires", time);
+        }
+    }
+
+    /**
+     * Session actively working on a pending media item. Pending items are
+     * expected to have a short lifetime, and owners should either
+     * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
+     * pending item within a few hours after first creating it.
+     */
+    public static class PendingSession implements AutoCloseable {
+        private final Context mContext;
+        private final Uri mUri;
+
+        /**
+         * Create a new pending session item to be published.
+         * @param context Contexxt of the application.
+         * @param uri Token which was previously returned from
+         *            {@link #createPending(Context, PendingParams)}.
+         */
+        PendingSession(Context context, Uri uri) {
+            mContext = Objects.requireNonNull(context);
+            mUri = Objects.requireNonNull(uri);
+        }
+
+        /**
+         * Open the underlying file representing this media item. When a media
+         * item is successfully completed, you should
+         * {@link ParcelFileDescriptor#close()} and then {@link #publish()} it.
+         *
+         * @return ParcelFileDescriptor to be written into.
+         */
+        public @NonNull ParcelFileDescriptor open() throws FileNotFoundException {
+            return mContext.getContentResolver().openFileDescriptor(mUri, "rw");
+        }
+
+        /**
+         * Open the underlying file representing this media item. When a media
+         * item is successfully completed, you should
+         * {@link OutputStream#close()} and then {@link #publish()} it.
+         *
+         * @return OutputStream to be written into.
+         */
+        public @NonNull OutputStream openOutputStream() throws FileNotFoundException {
+            return mContext.getContentResolver().openOutputStream(mUri);
+        }
+
+        /**
+         * When this media item is successfully completed, call this method to
+         * publish and make the final item visible to the user.
+         *
+         * @return the final {@code content://} Uri representing the newly
+         *         published media.
+         */
+        public @NonNull Uri publish() {
+            try {
+                final ContentValues values = new ContentValues();
+                setPendingContentValues(values, false);
+                values.putNull("date_expires");
+                mContext.getContentResolver().update(mUri, values, null, null);
+            } catch (Exception e) {
+                Log.e(TAG, "Unable to publish pending session.", e);
+            }
+            return mUri;
+        }
+
+        /**
+         * When this media item has failed to be completed, call this method to
+         * destroy the pending item record and any data related to it.
+         */
+        public void abandon() {
+            mContext.getContentResolver().delete(mUri, null, null);
+        }
+
+        @Override
+        public void close() {
+            // No resources to close, but at least we can inform people that no
+            // progress is being actively made.
+        }
+    }
+
+    /**
+     * Helper method to set the ContentValues to pending or non-pending.
+     * @param values ContentValues to be set.
+     * @param isPending Whether the item is pending.
+     */
+    private static void setPendingContentValues(ContentValues values, boolean isPending)
+            throws Exception {
+        values.put(MediaColumns.IS_PENDING, isPending ? 1 : 0);
+    }
+}
diff --git a/third_party/android_swipe_refresh/README.chromium b/third_party/android_swipe_refresh/README.chromium
index aa10d32..057fd8e8 100644
--- a/third_party/android_swipe_refresh/README.chromium
+++ b/third_party/android_swipe_refresh/README.chromium
@@ -6,8 +6,9 @@
 
 Description:
 This is a modified copy of the swipe refresh layout widget (and its two
-dependencies) from Android's app compat library (v4). The widget provides a
-pull-to-refresh styled layout for touch-activated refresh of view contents.
+dependencies) from Android's app compat library (v4). This has been
+migrated to use AndroidX libraries. The widget provides a pull-to-refresh
+styled layout for touch-activated refresh of view contents.
 
 Local Modifications:
 
diff --git a/third_party/android_swipe_refresh/java/src/org/chromium/third_party/android/swiperefresh/MaterialProgressDrawable.java b/third_party/android_swipe_refresh/java/src/org/chromium/third_party/android/swiperefresh/MaterialProgressDrawable.java
index 5913842..265c29c 100644
--- a/third_party/android_swipe_refresh/java/src/org/chromium/third_party/android/swiperefresh/MaterialProgressDrawable.java
+++ b/third_party/android_swipe_refresh/java/src/org/chromium/third_party/android/swiperefresh/MaterialProgressDrawable.java
@@ -16,11 +16,6 @@
 
 package org.chromium.third_party.android.swiperefresh;
 
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.Animation;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.Transformation;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -32,11 +27,17 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Animatable;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.graphics.drawable.Drawable;
 import android.util.DisplayMetrics;
 import android.view.View;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Animation;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.Transformation;
+
+import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/third_party/blink/perf_tests/css/OWNERS b/third_party/blink/perf_tests/css/OWNERS
new file mode 100644
index 0000000..dbb9dfd2
--- /dev/null
+++ b/third_party/blink/perf_tests/css/OWNERS
@@ -0,0 +1,2 @@
+file://third_party/blink/renderer/core/css/OWNERS
+
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index f98b53b..cb3b27e 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -321,6 +321,8 @@
     "web/web_embedded_worker_start_data.h",
     "web/web_external_popup_menu.h",
     "web/web_external_popup_menu_client.h",
+    "web/web_external_widget.h",
+    "web/web_external_widget_client.h",
     "web/web_form_control_element.h",
     "web/web_form_element.h",
     "web/web_frame.h",
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 2511123..fb2f7d2 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -2845,22 +2845,6 @@
       # Media features to emulate.
       optional array of MediaFeature features
 
-  # Emulates the given vision deficiency.
-  experimental command setEmulatedVisionDeficiency
-    parameters
-      # Vision deficiency to emulate.
-      enum type
-        none
-        achromatomaly
-        achromatopsia
-        blurredVision
-        deuteranomaly
-        deuteranopia
-        protanomaly
-        protanopia
-        tritanomaly
-        tritanopia
-
   # Overrides the Geolocation Position or Error. Omitting any of the parameters emulates position
   # unavailable.
   command setGeolocationOverride
diff --git a/third_party/blink/public/mojom/frame/frame.mojom b/third_party/blink/public/mojom/frame/frame.mojom
index 5542faa..0572f18 100644
--- a/third_party/blink/public/mojom/frame/frame.mojom
+++ b/third_party/blink/public/mojom/frame/frame.mojom
@@ -508,6 +508,11 @@
   // navigation.  This matches the in-process frame behavior.
   SetFrameOwnerProperties(FrameOwnerProperties properties);
 
+  // Updates the remote frame's replicated enforcement of insecure request
+  // policy. Used when the frame's policy is changed in another renderer
+  // process. Argument |policy| is a bitfield for InsecureRequestPolicy.
+  EnforceInsecureRequestPolicy(blink.mojom.InsecureRequestPolicy policy);
+
   // Update the replicated origin. Used when the frame is navigated to a
   // new origin.
   SetReplicatedOrigin(url.mojom.Origin origin,
diff --git a/third_party/blink/public/platform/task_type.h b/third_party/blink/public/platform/task_type.h
index 6efde7b..3438308 100644
--- a/third_party/blink/public/platform/task_type.h
+++ b/third_party/blink/public/platform/task_type.h
@@ -249,13 +249,14 @@
   kMainThreadTaskQueueControl = 43,
   kMainThreadTaskQueueCleanup = 52,
   kMainThreadTaskQueueMemoryPurge = 62,
+  kMainThreadTaskQueueNonWaking = 69,
   kCompositorThreadTaskQueueDefault = 45,
   kCompositorThreadTaskQueueInput = 49,
   kWorkerThreadTaskQueueDefault = 46,
   kWorkerThreadTaskQueueV8 = 47,
   kWorkerThreadTaskQueueCompositor = 48,
 
-  kCount = 69,
+  kCount = 70,
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/web/web_external_widget.h b/third_party/blink/public/web/web_external_widget.h
new file mode 100644
index 0000000..8dd77c7
--- /dev/null
+++ b/third_party/blink/public/web/web_external_widget.h
@@ -0,0 +1,38 @@
+// 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_EXTERNAL_WIDGET_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_EXTERNAL_WIDGET_H_
+
+#include "cc/layers/layer.h"
+#include "third_party/blink/public/web/web_external_widget_client.h"
+#include "third_party/blink/public/web/web_widget.h"
+
+namespace blink {
+
+// This is a type of Widget which is partially implemented outside of blink,
+// such as fullscreen pepper widgets. This interface provides methods for the
+// external implementation to access common Widget behaviour implemented inside
+// blink, in addition to the WebWidget methods. The blink Widget uses
+// WebExternalWidgetClient to communicate back to the external implementation.
+class WebExternalWidget : public WebWidget {
+ public:
+  // Create a new concrete instance of this class.
+  // |client| should be non-null.
+  // |debug_url| provides the return value for WebWidget::GetURLForDebugTrace.
+  BLINK_EXPORT static std::unique_ptr<WebExternalWidget> Create(
+      WebExternalWidgetClient* client,
+      const WebURL& debug_url);
+
+  virtual ~WebExternalWidget() = default;
+
+  // Provides an externally-created Layer to display as the widget's content, or
+  // a null pointer to remove any existing Layer which will cause the widget to
+  // display nothing.
+  virtual void SetRootLayer(scoped_refptr<cc::Layer>) = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_EXTERNAL_WIDGET_H_
diff --git a/third_party/blink/public/web/web_external_widget_client.h b/third_party/blink/public/web/web_external_widget_client.h
new file mode 100644
index 0000000..4fa83804
--- /dev/null
+++ b/third_party/blink/public/web/web_external_widget_client.h
@@ -0,0 +1,39 @@
+// 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_EXTERNAL_WIDGET_CLIENT_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_EXTERNAL_WIDGET_CLIENT_H_
+
+#include "third_party/blink/public/platform/web_input_event_result.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace blink {
+class WebCoalescedInputEvent;
+
+// The interface from blink to Widgets with implementations outside of blink.
+class WebExternalWidgetClient {
+ public:
+  virtual ~WebExternalWidgetClient() = default;
+
+  // Called when the associated WebExternalWidget receives input and
+  // needs the implementation to handle it.
+  virtual WebInputEventResult HandleInputEvent(
+      const WebCoalescedInputEvent&) = 0;
+
+  // Called when the associated WebExternalWidget wishes to dispatch
+  // any pending buffered touch events. The implementation may choose to buffer
+  // individual pointer events (received via HandleInputEvent) and dispatch
+  // a single touch event indicating the changes since the last touch event.
+  // This method is typically invoked once per frame whereas HandleInputEvent
+  // may be invoked many times per frame (i.e. multiple fingers on the touch
+  // surface).
+  virtual WebInputEventResult DispatchBufferedTouchEvents() = 0;
+
+  // Called when the associated WebExternalWidget has adjusted its size.
+  virtual void DidResize(const gfx::Size& size) = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_EXTERNAL_WIDGET_CLIENT_H_
diff --git a/third_party/blink/public/web/web_widget.h b/third_party/blink/public/web/web_widget.h
index 03123834..eb89afd 100644
--- a/third_party/blink/public/web/web_widget.h
+++ b/third_party/blink/public/web/web_widget.h
@@ -206,9 +206,6 @@
     return false;
   }
 
-  // Returns true if the WebWidget created is of type PepperWidget.
-  virtual bool IsPepperWidget() const { return false; }
-
   // Calling WebWidgetClient::requestPointerLock() will result in one
   // return call to didAcquirePointerLock() or didNotAcquirePointerLock().
   virtual void DidAcquirePointerLock() {}
diff --git a/third_party/blink/renderer/build/scripts/core/css/OWNERS b/third_party/blink/renderer/build/scripts/core/css/OWNERS
index 89abcdb..dbb9dfd2 100644
--- a/third_party/blink/renderer/build/scripts/core/css/OWNERS
+++ b/third_party/blink/renderer/build/scripts/core/css/OWNERS
@@ -1,7 +1,2 @@
 file://third_party/blink/renderer/core/css/OWNERS
 
-andruud@chromium.org
-futhark@chromium.org
-
-# TEAM: layout-dev@chromium.org
-# COMPONENT: Blink>CSS
diff --git a/third_party/blink/renderer/build/scripts/core/style/OWNERS b/third_party/blink/renderer/build/scripts/core/style/OWNERS
index 44d94a9..59049c3 100644
--- a/third_party/blink/renderer/build/scripts/core/style/OWNERS
+++ b/third_party/blink/renderer/build/scripts/core/style/OWNERS
@@ -1,7 +1,2 @@
 file://third_party/blink/renderer/core/style/OWNERS
 
-andruud@chromium.org
-futhark@chromium.org
-
-# TEAM: layout-dev@chromium.org
-# COMPONENT: Blink>CSS
diff --git a/third_party/blink/renderer/controller/memory_usage_monitor.cc b/third_party/blink/renderer/controller/memory_usage_monitor.cc
index ee61472..7ed800c7 100644
--- a/third_party/blink/renderer/controller/memory_usage_monitor.cc
+++ b/third_party/blink/renderer/controller/memory_usage_monitor.cc
@@ -7,6 +7,7 @@
 #include "base/test/test_mock_time_task_runner.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
 
 namespace blink {
@@ -16,7 +17,8 @@
 }
 
 MemoryUsageMonitor::MemoryUsageMonitor() {
-  timer_.SetTaskRunner(Thread::MainThread()->GetTaskRunner());
+  timer_.SetTaskRunner(
+      Thread::MainThread()->Scheduler()->NonWakingTaskRunner());
 }
 
 MemoryUsageMonitor::MemoryUsageMonitor(
diff --git a/third_party/blink/renderer/core/DEPS b/third_party/blink/renderer/core/DEPS
index 8759ba0..36bd76ad 100644
--- a/third_party/blink/renderer/core/DEPS
+++ b/third_party/blink/renderer/core/DEPS
@@ -53,6 +53,7 @@
     "+cc/paint/paint_flags.h",
     "+cc/paint/paint_worklet_input.h",
     "+cc/trees/browser_controls_params.h",
+    "+cc/trees/layer_tree_host.h",
     "+cc/trees/paint_holding_commit_trigger.h",
     "+cc/trees/layer_tree_host.h",
     "+components/performance_manager/public/mojom/coordination_unit.mojom-blink.h",
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index b70a955..8f8b6f4 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -584,8 +584,6 @@
     "style_traversal_root.h",
     "tree_scope_style_sheet_collection.cc",
     "tree_scope_style_sheet_collection.h",
-    "vision_deficiency.cc",
-    "vision_deficiency.h",
     "zoom_adjusted_pixel_value.h",
   ]
 }
diff --git a/third_party/blink/renderer/core/css/OWNERS b/third_party/blink/renderer/core/css/OWNERS
index d1eb1c3..a007a39 100644
--- a/third_party/blink/renderer/core/css/OWNERS
+++ b/third_party/blink/renderer/core/css/OWNERS
@@ -2,6 +2,7 @@
 andruud@chromium.org
 ericwilligers@chromium.org
 futhark@chromium.org
+xiaochengh@chromium.org
 
 # TEAM: layout-dev@chromium.org
 # COMPONENT: Blink>CSS
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 1605896..7293a782 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -737,8 +737,6 @@
   viewport_style->SetOverflowX(EOverflow::kAuto);
   viewport_style->SetOverflowY(EOverflow::kAuto);
 
-  document.GetStyleEngine().ApplyVisionDeficiencyStyle(viewport_style);
-
   return viewport_style;
 }
 
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 5ab331c8..97d2bfa 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -36,7 +36,6 @@
 #include "third_party/blink/renderer/core/css/css_font_selector.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
-#include "third_party/blink/renderer/core/css/css_uri_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/document_style_environment_variables.h"
 #include "third_party/blink/renderer/core/css/document_style_sheet_collector.h"
@@ -79,9 +78,7 @@
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
-#include "third_party/blink/renderer/core/style/filter_operations.h"
 #include "third_party/blink/renderer/core/style/style_initial_data.h"
-#include "third_party/blink/renderer/core/svg/svg_resource.h"
 #include "third_party/blink/renderer/core/svg/svg_style_element.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
 #include "third_party/blink/renderer/platform/fonts/font_selector.h"
@@ -1629,42 +1626,6 @@
                               kInvalidateCurrentScope, has_rebuilt_font_cache);
 }
 
-void StyleEngine::LoadVisionDeficiencyFilter() {
-  VisionDeficiency old_vision_deficiency = vision_deficiency_;
-  vision_deficiency_ = GetDocument().GetPage()->GetVisionDeficiency();
-  if (vision_deficiency_ == old_vision_deficiency)
-    return;
-
-  if (vision_deficiency_ == VisionDeficiency::kNoVisionDeficiency) {
-    vision_deficiency_filter_ = nullptr;
-  } else {
-    AtomicString url = CreateVisionDeficiencyFilterUrl(vision_deficiency_);
-    cssvalue::CSSURIValue css_uri_value(url);
-    SVGResource* svg_resource = css_uri_value.EnsureResourceReference();
-    // Note: The fact that we're using data: URLs here is an
-    // implementation detail. Emulating vision deficiencies should still
-    // work even if the Document's Content-Security-Policy disallows
-    // data: URLs.
-    svg_resource->LoadWithoutCSP(GetDocument());
-    vision_deficiency_filter_ =
-        MakeGarbageCollected<ReferenceFilterOperation>(url, svg_resource);
-  }
-}
-
-void StyleEngine::VisionDeficiencyChanged() {
-  MarkViewportStyleDirty();
-}
-
-void StyleEngine::ApplyVisionDeficiencyStyle(
-    scoped_refptr<ComputedStyle> layout_view_style) {
-  LoadVisionDeficiencyFilter();
-  if (vision_deficiency_filter_) {
-    FilterOperations ops;
-    ops.Operations().push_back(vision_deficiency_filter_);
-    layout_view_style->SetFilter(ops);
-  }
-}
-
 const MediaQueryEvaluator& StyleEngine::EnsureMediaQueryEvaluator() {
   if (!media_query_evaluator_) {
     if (GetDocument().GetFrame()) {
@@ -2174,7 +2135,6 @@
   visitor->Trace(active_tree_scopes_);
   visitor->Trace(tree_boundary_crossing_scopes_);
   visitor->Trace(resolver_);
-  visitor->Trace(vision_deficiency_filter_);
   visitor->Trace(viewport_resolver_);
   visitor->Trace(media_query_evaluator_);
   visitor->Trace(global_rule_set_);
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 05a79dfd..e750feac 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -51,7 +51,6 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/tree_ordered_list.h"
 #include "third_party/blink/renderer/core/html/track/text_track.h"
-#include "third_party/blink/renderer/core/style/filter_operations.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
 #include "third_party/blink/renderer/platform/fonts/font_selector_client.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -81,7 +80,7 @@
 
 // The StyleEngine class manages style-related state for the document. There is
 // a 1-1 relationship of Document to StyleEngine. The document calls the
-// StyleEngine when the document is updated in a way that impacts styles.
+// StyleEngine when the the document is updated in a way that impacts styles.
 class CORE_EXPORT StyleEngine final : public GarbageCollected<StyleEngine>,
                                       public FontSelectorClient,
                                       public NameClient {
@@ -325,10 +324,6 @@
   void ApplyUserRuleSetChanges(const ActiveStyleSheetVector& old_style_sheets,
                                const ActiveStyleSheetVector& new_style_sheets);
 
-  void VisionDeficiencyChanged();
-  void ApplyVisionDeficiencyStyle(
-      scoped_refptr<ComputedStyle> layout_view_style);
-
   void CollectMatchingUserRules(ElementRuleCollector&) const;
 
   void CustomPropertyRegistered();
@@ -391,8 +386,6 @@
   // FontSelectorClient implementation.
   void FontsNeedUpdate(FontSelector*) override;
 
-  void LoadVisionDeficiencyFilter();
-
  private:
   bool NeedsActiveStyleSheetUpdate() const {
     return all_tree_scopes_dirty_ || tree_scopes_removed_ ||
@@ -545,9 +538,6 @@
   bool viewport_style_dirty_ = false;
   bool fonts_need_update_ = false;
 
-  VisionDeficiency vision_deficiency_ = VisionDeficiency::kNoVisionDeficiency;
-  Member<ReferenceFilterOperation> vision_deficiency_filter_;
-
   Member<StyleResolver> resolver_;
   Member<ViewportStyleResolver> viewport_resolver_;
   Member<MediaQueryEvaluator> media_query_evaluator_;
diff --git a/third_party/blink/renderer/core/css/vision_deficiency.cc b/third_party/blink/renderer/core/css/vision_deficiency.cc
deleted file mode 100644
index 66e9d39..0000000
--- a/third_party/blink/renderer/core/css/vision_deficiency.cc
+++ /dev/null
@@ -1,100 +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.
-
-#include "third_party/blink/renderer/core/css/vision_deficiency.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-AtomicString CreateFilterDataUrl(AtomicString piece) {
-  // clang-format off
-  AtomicString url =
-      "data:image/svg+xml,"
-        "<svg xmlns=\"http://www.w3.org/2000/svg\">"
-          "<filter id=\"f\">" +
-            piece +
-          "</filter>"
-        "</svg>"
-      "#f";
-  // clang-format on
-  return url;
-}
-
-AtomicString CreateVisionDeficiencyFilterUrl(
-    VisionDeficiency vision_deficiency) {
-  switch (vision_deficiency) {
-    case VisionDeficiency::kAchromatomaly:
-      return CreateFilterDataUrl(
-          "<feColorMatrix values=\""
-          "0.618 0.320 0.062 0.000 0.000 "
-          "0.163 0.775 0.062 0.000 0.000 "
-          "0.163 0.320 0.516 0.000 0.000 "
-          "0.000 0.000 0.000 1.000 0.000 "
-          "\"/>");
-    case VisionDeficiency::kAchromatopsia:
-      return CreateFilterDataUrl(
-          "<feColorMatrix values=\""
-          "0.299 0.587 0.114 0.000 0.000 "
-          "0.299 0.587 0.114 0.000 0.000 "
-          "0.299 0.587 0.114 0.000 0.000 "
-          "0.000 0.000 0.000 1.000 0.000 "
-          "\"/>");
-    case VisionDeficiency::kBlurredVision:
-      return CreateFilterDataUrl(
-          "<feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"2\"/>");
-    case VisionDeficiency::kDeuteranomaly:
-      return CreateFilterDataUrl(
-          "<feColorMatrix values=\""
-          "0.800 0.200 0.000 0.000 0.000 "
-          "0.258 0.742 0.000 0.000 0.000 "
-          "0.000 0.142 0.858 0.000 0.000 "
-          "0.000 0.000 0.000 1.000 0.000 "
-          "\"/>");
-    case VisionDeficiency::kDeuteranopia:
-      return CreateFilterDataUrl(
-          "<feColorMatrix values=\""
-          "0.625 0.375 0.000 0.000 0.000 "
-          "0.700 0.300 0.000 0.000 0.000 "
-          "0.000 0.300 0.700 0.000 0.000 "
-          "0.000 0.000 0.000 1.000 0.000 "
-          "\"/>");
-    case VisionDeficiency::kProtanomaly:
-      return CreateFilterDataUrl(
-          "<feColorMatrix values=\""
-          "0.817 0.183 0.000 0.000 0.000 "
-          "0.333 0.667 0.000 0.000 0.000 "
-          "0.000 0.125 0.875 0.000 0.000 "
-          "0.000 0.000 0.000 1.000 0.000 "
-          "\"/>");
-    case VisionDeficiency::kProtanopia:
-      return CreateFilterDataUrl(
-          "<feColorMatrix values=\""
-          "0.567 0.433 0.000 0.000 0.000 "
-          "0.558 0.442 0.000 0.000 0.000 "
-          "0.000 0.242 0.758 0.000 0.000 "
-          "0.000 0.000 0.000 1.000 0.000 "
-          "\"/>");
-    case VisionDeficiency::kTritanomaly:
-      return CreateFilterDataUrl(
-          "<feColorMatrix values=\""
-          "0.967 0.033 0.000 0.000 0.000 "
-          "0.000 0.733 0.267 0.000 0.000 "
-          "0.000 0.183 0.817 0.000 0.000 "
-          "0.000 0.000 0.000 1.000 0.000 "
-          "\"/>");
-    case VisionDeficiency::kTritanopia:
-      return CreateFilterDataUrl(
-          "<feColorMatrix values=\""
-          "0.950 0.050 0.000 0.000 0.000 "
-          "0.000 0.433 0.567 0.000 0.000 "
-          "0.000 0.475 0.525 0.000 0.000 "
-          "0.000 0.000 0.000 1.000 0.000 "
-          "\"/>");
-    case VisionDeficiency::kNoVisionDeficiency:
-      NOTREACHED();
-      return "";
-  }
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/vision_deficiency.h b/third_party/blink/renderer/core/css/vision_deficiency.h
deleted file mode 100644
index b91d422..0000000
--- a/third_party/blink/renderer/core/css/vision_deficiency.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 THIRD_PARTY_BLINK_RENDERER_CORE_CSS_VISION_DEFICIENCY_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_VISION_DEFICIENCY_H_
-
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-enum class VisionDeficiency {
-  kNoVisionDeficiency,
-  kAchromatomaly,
-  kAchromatopsia,
-  kBlurredVision,
-  kDeuteranomaly,
-  kDeuteranopia,
-  kProtanomaly,
-  kProtanopia,
-  kTritanomaly,
-  kTritanopia,
-};
-
-AtomicString CreateVisionDeficiencyFilterUrl(
-    VisionDeficiency vision_deficiency);
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_VISION_DEFICIENCY_H_
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index fa3b053..87cfca1a 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -8606,10 +8606,6 @@
   MediaQueryAffectingValueChanged();
 }
 
-void Document::VisionDeficiencyChanged() {
-  GetStyleEngine().VisionDeficiencyChanged();
-}
-
 void Document::UpdateForcedColors() {
   auto* web_theme_engine =
       RuntimeEnabledFeatures::ForcedColorsEnabled() && Platform::Current()
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index e2ca926..de5330e 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -44,7 +44,6 @@
 #include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h"
 #include "third_party/blink/renderer/core/accessibility/axid.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/css/vision_deficiency.h"
 #include "third_party/blink/renderer/core/dom/container_node.h"
 #include "third_party/blink/renderer/core/dom/create_element_flags.h"
 #include "third_party/blink/renderer/core/dom/document_encoding_data.h"
@@ -1699,9 +1698,6 @@
 
   void ColorSchemeChanged();
 
-  // A new vision deficiency is being emulated through DevTools.
-  void VisionDeficiencyChanged();
-
   void ClearIsolatedWorldCSPForTesting(int32_t world_id);
 
   // A META element with name=color-scheme was added, removed, or modified.
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 5bbe367..99e7f8f 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -4805,6 +4805,16 @@
   return nullptr;
 }
 
+void Element::HideNonce() {
+  const AtomicString& nonce_value = FastGetAttribute(html_names::kNonceAttr);
+  if (nonce_value.IsEmpty())
+    return;
+  if (!InActiveDocument())
+    return;
+  if (GetDocument().GetContentSecurityPolicy()->HasHeaderDeliveredPolicy())
+    setAttribute(html_names::kNonceAttr, g_empty_atom);
+}
+
 ElementIntersectionObserverData* Element::IntersectionObserverData() const {
   if (HasRareData())
     return GetElementRareData()->IntersectionObserverData();
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index a2ff655..580d51a 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -1013,6 +1013,17 @@
 
   virtual void ParserDidSetAttributes() {}
 
+  // The "nonce" attribute is hidden when:
+  // 1) The Content-Security-Policy is delivered from the HTTP headers.
+  // 2) The Element is part of the active document.
+  // See https://github.com/whatwg/html/pull/2373
+  //
+  // This applies to the element of the HTML and SVG namespaces.
+  //
+  // This function clears the "nonce" attribute whenever conditions (1) and (2)
+  // are met.
+  void HideNonce();
+
  private:
   void ScrollLayoutBoxBy(const ScrollToOptions*);
   void ScrollLayoutBoxTo(const ScrollToOptions*);
diff --git a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
index 20fd026..625c561 100644
--- a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
+++ b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
@@ -140,8 +140,8 @@
   event_dispatched_ = true;
 #endif
   if (GetEvent().GetEventPath().IsEmpty()) {
-    // eventPath() can be empty if event path is shrinked by relataedTarget
-    // retargeting.
+    // eventPath() can be empty if relatedTarget retargeting has shrunk the
+    // path.
     return DispatchEventResult::kNotCanceled;
   }
   std::unique_ptr<EventTiming> eventTiming;
diff --git a/third_party/blink/renderer/core/dom/idle_deadline_test.cc b/third_party/blink/renderer/core/dom/idle_deadline_test.cc
index 8116102..71dc4d7 100644
--- a/third_party/blink/renderer/core/dom/idle_deadline_test.cc
+++ b/third_party/blink/renderer/core/dom/idle_deadline_test.cc
@@ -42,6 +42,9 @@
   scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override {
     return nullptr;
   }
+  scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override {
+    return nullptr;
+  }
   scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
       override {
     return nullptr;
diff --git a/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc b/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
index fb61aec..911c7e2c 100644
--- a/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
+++ b/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
@@ -33,6 +33,9 @@
   scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override {
     return nullptr;
   }
+  scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override {
+    return nullptr;
+  }
   scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
       override {
     return nullptr;
diff --git a/third_party/blink/renderer/core/exported/BUILD.gn b/third_party/blink/renderer/core/exported/BUILD.gn
index f807bf8..8c2a7601 100644
--- a/third_party/blink/renderer/core/exported/BUILD.gn
+++ b/third_party/blink/renderer/core/exported/BUILD.gn
@@ -24,6 +24,8 @@
     "web_dom_message_event.cc",
     "web_element.cc",
     "web_element_collection.cc",
+    "web_external_widget_impl.cc",
+    "web_external_widget_impl.h",
     "web_form_control_element.cc",
     "web_form_element.cc",
     "web_form_element_observer_impl.cc",
diff --git a/third_party/blink/renderer/core/exported/web_external_widget_impl.cc b/third_party/blink/renderer/core/exported/web_external_widget_impl.cc
new file mode 100644
index 0000000..80ee313
--- /dev/null
+++ b/third_party/blink/renderer/core/exported/web_external_widget_impl.cc
@@ -0,0 +1,64 @@
+// 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 "third_party/blink/renderer/core/exported/web_external_widget_impl.h"
+
+#include "cc/trees/layer_tree_host.h"
+
+namespace blink {
+
+std::unique_ptr<WebExternalWidget> WebExternalWidget::Create(
+    WebExternalWidgetClient* client,
+    const blink::WebURL& debug_url) {
+  return std::make_unique<WebExternalWidgetImpl>(client, debug_url);
+}
+
+WebExternalWidgetImpl::WebExternalWidgetImpl(WebExternalWidgetClient* client,
+                                             const WebURL& debug_url)
+    : client_(client), debug_url_(debug_url) {
+  DCHECK(client_);
+}
+
+WebExternalWidgetImpl::~WebExternalWidgetImpl() = default;
+
+void WebExternalWidgetImpl::SetCompositorHosts(
+    cc::LayerTreeHost* layer_tree_host,
+    cc::AnimationHost* animation_host) {
+  widget_base_.SetCompositorHosts(layer_tree_host, animation_host);
+}
+
+WebHitTestResult WebExternalWidgetImpl::HitTestResultAt(const gfx::Point&) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+WebURL WebExternalWidgetImpl::GetURLForDebugTrace() {
+  return debug_url_;
+}
+
+WebSize WebExternalWidgetImpl::Size() {
+  return size_;
+}
+
+void WebExternalWidgetImpl::Resize(const WebSize& size) {
+  if (size_ == size)
+    return;
+  size_ = size;
+  client_->DidResize(gfx::Size(size));
+}
+
+WebInputEventResult WebExternalWidgetImpl::HandleInputEvent(
+    const WebCoalescedInputEvent& coalesced_event) {
+  return client_->HandleInputEvent(coalesced_event);
+}
+
+WebInputEventResult WebExternalWidgetImpl::DispatchBufferedTouchEvents() {
+  return client_->DispatchBufferedTouchEvents();
+}
+
+void WebExternalWidgetImpl::SetRootLayer(scoped_refptr<cc::Layer> layer) {
+  widget_base_.LayerTreeHost()->SetNonBlinkManagedRootLayer(layer);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_external_widget_impl.h b/third_party/blink/renderer/core/exported/web_external_widget_impl.h
new file mode 100644
index 0000000..60ad34f
--- /dev/null
+++ b/third_party/blink/renderer/core/exported/web_external_widget_impl.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EXPORTED_WEB_EXTERNAL_WIDGET_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_EXPORTED_WEB_EXTERNAL_WIDGET_IMPL_H_
+
+#include "third_party/blink/public/web/web_external_widget.h"
+
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/renderer/platform/widget/widget_base.h"
+
+namespace blink {
+
+class WebExternalWidgetImpl : public WebExternalWidget {
+ public:
+  WebExternalWidgetImpl(WebExternalWidgetClient* client,
+                        const WebURL& debug_url);
+  ~WebExternalWidgetImpl() override;
+
+  // WebWidget overrides:
+  void SetCompositorHosts(cc::LayerTreeHost*, cc::AnimationHost*) override;
+  WebHitTestResult HitTestResultAt(const gfx::Point&) override;
+  WebURL GetURLForDebugTrace() override;
+  WebSize Size() override;
+  void Resize(const WebSize& size) override;
+  WebInputEventResult HandleInputEvent(
+      const WebCoalescedInputEvent& coalesced_event) override;
+  WebInputEventResult DispatchBufferedTouchEvents() override;
+
+  // WebExternalWidget overrides:
+  void SetRootLayer(scoped_refptr<cc::Layer>) override;
+
+ private:
+  WebExternalWidgetClient* const client_;
+  const WebURL debug_url_;
+  WebSize size_;
+  WidgetBase widget_base_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EXPORTED_WEB_EXTERNAL_WIDGET_IMPL_H_
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
index 534c694..5cbb3af 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
@@ -139,6 +139,14 @@
               uma_percentage_name.ToString().Utf8().c_str(), 0, 10000000, 50));
     }
   }
+
+  // Make space in the current sample.
+  current_sample_.sub_metrics_durations.Grow(static_cast<wtf_size_t>(kCount));
+  current_sample_.sub_metric_percentages.Grow(static_cast<wtf_size_t>(kCount));
+}
+
+LocalFrameUkmAggregator::~LocalFrameUkmAggregator() {
+  ReportUpdateTimeEvent();
 }
 
 LocalFrameUkmAggregator::ScopedUkmHierarchicalTimer
@@ -205,10 +213,10 @@
   if (!calls_to_next_forced_style_layout_uma_) {
     auto& record = absolute_metric_records_[kForcedStyleAndLayout];
     record.uma_counter->CountMicroseconds(duration);
-    if (is_before_fcp_)
-      record.pre_fcp_uma_counter->CountMicroseconds(duration);
-    else
+    if (fcp_state_ == kHavePassedFCP)
       record.post_fcp_uma_counter->CountMicroseconds(duration);
+    else
+      record.pre_fcp_uma_counter->CountMicroseconds(duration);
     calls_to_next_forced_style_layout_uma_ =
         base::RandInt(0, mean_calls_between_forced_style_layout_uma_ * 2);
   } else {
@@ -219,69 +227,27 @@
 
 void LocalFrameUkmAggregator::DidReachFirstContentfulPaint(
     bool are_painting_main_frame) {
-  DCHECK(is_before_fcp_);
-
-  is_before_fcp_ = false;
+  DCHECK(fcp_state_ != kHavePassedFCP);
 
   if (!are_painting_main_frame) {
     DCHECK(AllMetricsAreZero());
     return;
   }
 
-#define CASE_FOR_ID(name)                                                  \
-  case k##name:                                                            \
-    builder.Set##name(absolute_record.pre_fcp_aggregate.InMicroseconds()); \
-    break
-
-  ukm::builders::Blink_PageLoad builder(source_id_);
-  builder.SetMainFrame(primary_metric_.pre_fcp_aggregate.InMicroseconds());
-  primary_metric_.uma_aggregate_counter->CountMicroseconds(
-      primary_metric_.pre_fcp_aggregate);
-  for (unsigned i = 0; i < (unsigned)kCount; ++i) {
-    auto& absolute_record = absolute_metric_records_[i];
-    if (absolute_record.uma_aggregate_counter) {
-      absolute_record.uma_aggregate_counter->CountMicroseconds(
-          absolute_record.pre_fcp_aggregate);
-    }
-
-    switch (static_cast<MetricId>(i)) {
-      CASE_FOR_ID(Compositing);
-      CASE_FOR_ID(CompositingCommit);
-      CASE_FOR_ID(ImplCompositorCommit);
-      CASE_FOR_ID(IntersectionObservation);
-      CASE_FOR_ID(Paint);
-      CASE_FOR_ID(PrePaint);
-      CASE_FOR_ID(Style);
-      CASE_FOR_ID(Layout);
-      CASE_FOR_ID(ForcedStyleAndLayout);
-      CASE_FOR_ID(HitTestDocumentUpdate);
-      CASE_FOR_ID(ScrollingCoordinator);
-      CASE_FOR_ID(HandleInputEvents);
-      CASE_FOR_ID(Animate);
-      CASE_FOR_ID(UpdateLayers);
-      CASE_FOR_ID(ProxyCommit);
-      CASE_FOR_ID(WaitForCommit);
-      case kCount:
-      case kMainFrame:
-        NOTREACHED();
-        break;
-    }
-  }
-  builder.Record(recorder_);
-
-#undef CASE_FOR_ID
+  fcp_state_ = kThisFrameReachedFCP;
 }
 
 void LocalFrameUkmAggregator::RecordSample(size_t metric_index,
                                            base::TimeTicks start,
                                            base::TimeTicks end) {
   base::TimeDelta duration = end - start;
+  bool is_pre_fcp = (fcp_state_ != kHavePassedFCP);
 
   // Accumulate for UKM and record the UMA
   DCHECK_LT(metric_index, absolute_metric_records_.size());
   auto& record = absolute_metric_records_[metric_index];
   record.interval_duration += duration;
-  if (is_before_fcp_)
+  if (is_pre_fcp)
     record.pre_fcp_aggregate += duration;
   // Record the UMA
   // ForcedStyleAndLayout happen so frequently on some pages that we overflow
@@ -292,7 +258,7 @@
       RecordForcedStyleLayoutUMA(duration);
     } else {
       record.uma_counter->CountMicroseconds(duration);
-      if (is_before_fcp_) {
+      if (is_pre_fcp) {
         record.pre_fcp_uma_counter->CountMicroseconds(duration);
       } else {
         record.post_fcp_uma_counter->CountMicroseconds(duration);
@@ -347,17 +313,19 @@
   in_main_frame_update_ = false;
 
   base::TimeDelta duration = end - start;
+  bool report_as_pre_fcp = (fcp_state_ != kHavePassedFCP);
+  bool report_fcp_metrics = (fcp_state_ == kThisFrameReachedFCP);
 
   // Record UMA
   primary_metric_.uma_counter->CountMicroseconds(duration);
-  if (is_before_fcp_)
+  if (report_as_pre_fcp)
     primary_metric_.pre_fcp_uma_counter->CountMicroseconds(duration);
   else
     primary_metric_.post_fcp_uma_counter->CountMicroseconds(duration);
 
   // Record primary time information
   primary_metric_.interval_duration = duration;
-  if (is_before_fcp_)
+  if (report_as_pre_fcp)
     primary_metric_.pre_fcp_aggregate += duration;
 
   // Compute all the dependent metrics, after finding which bucket we're in
@@ -378,43 +346,76 @@
 
   // Record here to avoid resetting the ratios before this data point is
   // recorded.
-  UpdateEventTimeAndRecordEventIfNeeded(trackers);
+  UpdateEventTimeAndUpdateSampleIfNeeded(trackers);
+
+  // Report the FCP metrics, if necessary, after updating the sample so that
+  // the sample has been recorded for the frame that produced FCP.
+  if (report_fcp_metrics) {
+    ReportPreFCPEvent();
+    ReportUpdateTimeEvent();
+    // Update the state to prevent future reporting.
+    fcp_state_ = kHavePassedFCP;
+  }
 
   // Reset for the next frame.
   ResetAllMetrics();
 }
 
-void LocalFrameUkmAggregator::UpdateEventTimeAndRecordEventIfNeeded(
+void LocalFrameUkmAggregator::UpdateEventTimeAndUpdateSampleIfNeeded(
     cc::ActiveFrameSequenceTrackers trackers) {
-  // TODO(schenney) Adjust the mean sample interval so that we do not
-  // get our events throttled by the UKM system. For M-73 only 1 in 212
-  // events are being sent.
-  if (!frames_to_next_event_) {
-    RecordEvent(trackers);
-    frames_to_next_event_ += SampleFramesToNextEvent();
+  // Update the frame count first, because it must include this frame
+  frames_since_last_report_++;
+
+  // Regardless of test requests, always capture the first frame.
+  if (frames_since_last_report_ == 1) {
+    UpdateSample(trackers);
+    return;
   }
-  DCHECK_GT(frames_to_next_event_, 0u);
-  --frames_to_next_event_;
+
+  // Exit if in testing and we do not want to update this frame
+  if (next_frame_sample_control_for_test_ == kMustNotChooseNextFrame)
+    return;
+
+  // Update the sample with probability 1/frames_since_last_report_, or if
+  // testing demand is.
+  if ((next_frame_sample_control_for_test_ == kMustChooseNextFrame) ||
+      base::RandDouble() < 1 / static_cast<double>(frames_since_last_report_)) {
+    UpdateSample(trackers);
+  }
 }
 
-void LocalFrameUkmAggregator::RecordEvent(
+void LocalFrameUkmAggregator::UpdateSample(
     cc::ActiveFrameSequenceTrackers trackers) {
-#define CASE_FOR_ID(name)                                                 \
-  case k##name:                                                           \
-    builder.Set##name(absolute_record.interval_duration.InMicroseconds()) \
-        .Set##name##Percentage(percentage);                               \
+  current_sample_.primary_metric_duration = primary_metric_.interval_duration;
+  float primary_metric_in_microseconds =
+      primary_metric_.interval_duration.InMicrosecondsF();
+  for (unsigned i = 0; i < static_cast<unsigned>(kCount); ++i) {
+    current_sample_.sub_metrics_durations[i] =
+        absolute_metric_records_[i].interval_duration;
+    current_sample_.sub_metric_percentages[i] = static_cast<unsigned>(floor(
+        main_frame_percentage_records_[i].interval_duration.InMicrosecondsF() *
+        100.0 / primary_metric_in_microseconds));
+  }
+  current_sample_.trackers = trackers;
+}
+
+void LocalFrameUkmAggregator::ReportPreFCPEvent() {
+#define CASE_FOR_ID(name)                                                  \
+  case k##name:                                                            \
+    builder.Set##name(absolute_record.pre_fcp_aggregate.InMicroseconds()); \
     break
 
-  ukm::builders::Blink_UpdateTime builder(source_id_);
-  builder.SetMainFrame(primary_metric_.interval_duration.InMicroseconds());
-  builder.SetMainFrameIsBeforeFCP(is_before_fcp_);
-  builder.SetMainFrameReasons(trackers);
+  ukm::builders::Blink_PageLoad builder(source_id_);
+  builder.SetMainFrame(primary_metric_.pre_fcp_aggregate.InMicroseconds());
+  primary_metric_.uma_aggregate_counter->CountMicroseconds(
+      primary_metric_.pre_fcp_aggregate);
   for (unsigned i = 0; i < static_cast<unsigned>(kCount); ++i) {
     auto& absolute_record = absolute_metric_records_[i];
-    auto& percentage_record = main_frame_percentage_records_[i];
-    unsigned percentage = static_cast<unsigned>(
-        floor(percentage_record.interval_duration.InMicrosecondsF() * 100.0 /
-              primary_metric_.interval_duration.InMicrosecondsF()));
+    if (absolute_record.uma_aggregate_counter) {
+      absolute_record.uma_aggregate_counter->CountMicroseconds(
+          absolute_record.pre_fcp_aggregate);
+    }
+
     switch (static_cast<MetricId>(i)) {
       CASE_FOR_ID(Compositing);
       CASE_FOR_ID(CompositingCommit);
@@ -442,6 +443,55 @@
 #undef CASE_FOR_ID
 }
 
+void LocalFrameUkmAggregator::ReportUpdateTimeEvent() {
+  // Don't report if we haven't generated any samples.
+  if (!frames_since_last_report_)
+    return;
+
+#define CASE_FOR_ID(name, index)                                               \
+  case k##name:                                                                \
+    builder                                                                    \
+        .Set##name(                                                            \
+            current_sample_.sub_metrics_durations[index].InMicroseconds())     \
+        .Set##name##Percentage(current_sample_.sub_metric_percentages[index]); \
+    break
+
+  ukm::builders::Blink_UpdateTime builder(source_id_);
+  builder.SetMainFrame(
+      current_sample_.primary_metric_duration.InMicroseconds());
+  builder.SetMainFrameIsBeforeFCP(fcp_state_ != kHavePassedFCP);
+  builder.SetMainFrameReasons(current_sample_.trackers);
+  for (unsigned i = 0; i < static_cast<unsigned>(kCount); ++i) {
+    switch (static_cast<MetricId>(i)) {
+      CASE_FOR_ID(Compositing, i);
+      CASE_FOR_ID(CompositingCommit, i);
+      CASE_FOR_ID(ImplCompositorCommit, i);
+      CASE_FOR_ID(IntersectionObservation, i);
+      CASE_FOR_ID(Paint, i);
+      CASE_FOR_ID(PrePaint, i);
+      CASE_FOR_ID(Style, i);
+      CASE_FOR_ID(Layout, i);
+      CASE_FOR_ID(ForcedStyleAndLayout, i);
+      CASE_FOR_ID(HitTestDocumentUpdate, i);
+      CASE_FOR_ID(ScrollingCoordinator, i);
+      CASE_FOR_ID(HandleInputEvents, i);
+      CASE_FOR_ID(Animate, i);
+      CASE_FOR_ID(UpdateLayers, i);
+      CASE_FOR_ID(ProxyCommit, i);
+      CASE_FOR_ID(WaitForCommit, i);
+      case kCount:
+      case kMainFrame:
+        NOTREACHED();
+        break;
+    }
+  }
+  builder.Record(recorder_);
+#undef CASE_FOR_ID
+
+  // Reset the frames since last report to ensure correct sampling.
+  frames_since_last_report_ = 0;
+}
+
 void LocalFrameUkmAggregator::ResetAllMetrics() {
   primary_metric_.reset();
   for (auto& record : absolute_metric_records_)
@@ -450,38 +500,6 @@
     record.reset();
 }
 
-unsigned LocalFrameUkmAggregator::SampleFramesToNextEvent() {
-  // Return the test interval if set
-  if (frames_to_next_event_for_test_)
-    return frames_to_next_event_for_test_;
-
-  // Sample from an exponential distribution to give a poisson distribution
-  // of samples per time unit, then weigh it with an exponential multiplier to
-  // give a few samples in rapid succession (for frames early in the page's
-  // life) then exponentially fewer as the page lives longer.
-  // RandDouble() returns [0,1), but we need (0,1]. If RandDouble() is
-  // uniformly random, so is 1-RandDouble(), so use it to adjust the range.
-  // When RandDouble returns 0.0, as it could, we will get a float_sample of
-  // 0, causing underflow in UpdateEventTimeAndRecordIfNeeded. So rejection
-  // sample until we get a positive count.
-  double float_sample = 0;
-  do {
-    float_sample = -(sample_rate_multiplier_ *
-                     std::exp(samples_so_far_ * sample_decay_rate_) *
-                     std::log(1.0 - base::RandDouble()));
-  } while (float_sample == 0);
-  // float_sample is positive, so we don't need to worry about underflow.
-  // After around 30 samples we will end up with a super high
-  // sample. That's OK because it just means we'll stop reporting metrics
-  // for that session, but we do need to be careful about overflow and NaN.
-  samples_so_far_++;
-  unsigned unsigned_sample =
-      std::isnan(float_sample)
-          ? UINT_MAX
-          : base::saturated_cast<unsigned>(std::ceil(float_sample));
-  return unsigned_sample;
-}
-
 bool LocalFrameUkmAggregator::AllMetricsAreZero() {
   if (primary_metric_.interval_duration.InMicroseconds())
     return false;
@@ -493,4 +511,12 @@
   return true;
 }
 
+void LocalFrameUkmAggregator::ChooseNextFrameForTest() {
+  next_frame_sample_control_for_test_ = kMustChooseNextFrame;
+}
+
+void LocalFrameUkmAggregator::DoNotChooseNextFrameForTest() {
+  next_frame_sample_control_for_test_ = kMustNotChooseNextFrame;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
index b25aa46..ef31447 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
@@ -35,7 +35,7 @@
 // - recorder: UkmRecorder which will handle the events
 //
 // The aggregator manages all of the UKM and UMA names for LocalFrameView.
-// It constructs and takes ownership the UMA counters when constructed
+// It constructs and takes ownership of the UMA counters when constructed
 // itself. We do this to localize all UMA and UKM metrics in one place, so
 // that adding a metric is localized to the cc file of this class, protected
 // from errors that might arise when adding names in multiple places.
@@ -53,12 +53,11 @@
 // primary time and computes metrics that depend on it. UMA metrics are updated
 // at this time.
 //
-// A UKM event is generated according to a sampling strategy. A record is always
-// generated on the first lifecycle update, and then additional samples are
-// taken at random frames simulating a poisson process with mean number of
-// frames between events of mean_frames_between_samples_. The first primary
-// metric recording after the frame count has passed will produce an event with
-// all the data for that frame (i.e. the period since the last BeginMainFrame).
+// A UKM event is generated according to a sampling strategy, with the goal
+// being to choose one frame to report before First Contentful Paint and
+// one frame to report during the subsequent document lifetime. We maintain
+// a copy of the current sample, and randomly choose to update it on each frame
+// such that any given frame is equally likely to be the final sample.
 //
 // Sample usage (see also SCOPED_UMA_AND_UKM_TIMER):
 //   std::unique_ptr<UkmHierarchicalTimeAggregator> aggregator(
@@ -224,7 +223,7 @@
   };
 
   LocalFrameUkmAggregator(int64_t source_id, ukm::UkmRecorder*);
-  ~LocalFrameUkmAggregator() = default;
+  ~LocalFrameUkmAggregator();
 
   // Create a scoped timer with the index of the metric. Note the index must
   // correspond to the matching index in metric_names.
@@ -259,8 +258,9 @@
   void BeginMainFrame();
 
   // Inform the aggregator that we have reached First Contentful Paint.
-  // The UKM event reports this and UMA for aggregated contributions to
-  // FCP are reported if are_painting_main_frame is true.
+  // The UKM event for the pre-FCP period will be recorded and UMA for
+  // aggregated contributions to FCP are reported if are_painting_main_frame
+  // is true.
   void DidReachFirstContentfulPaint(bool are_painting_main_frame);
 
   bool InMainFrameUpdate() { return in_main_frame_update_; }
@@ -296,22 +296,36 @@
     void reset() { interval_duration = base::TimeDelta(); }
   };
 
-  void UpdateEventTimeAndRecordEventIfNeeded(
+  struct SampleToRecord {
+    base::TimeDelta primary_metric_duration;
+    Vector<base::TimeDelta> sub_metrics_durations;
+    Vector<unsigned> sub_metric_percentages;
+    cc::ActiveFrameSequenceTrackers trackers;
+  };
+
+  void UpdateEventTimeAndUpdateSampleIfNeeded(
       cc::ActiveFrameSequenceTrackers trackers);
-  void RecordEvent(cc::ActiveFrameSequenceTrackers trackers);
+  void UpdateSample(cc::ActiveFrameSequenceTrackers trackers);
   void ResetAllMetrics();
-  unsigned SampleFramesToNextEvent();
+
+  // Reports the current sample to the UKM system. Called on the first main
+  // frame update after First Contentful Paint and at destruction. Also resets
+  // the frame count.
+  void ReportUpdateTimeEvent();
+
+  // Reports the Blink.PageLoad to the UKM system. Called on the first main
+  // frame after First Contentful Paint.
+  void ReportPreFCPEvent();
 
   // Implements throttling of the ForcedStyleAndLayoutUMA metric.
   void RecordForcedStyleLayoutUMA(base::TimeDelta& duration);
 
-  // To test event sampling. This and all future intervals will be the given
-  // frame count, until this is called again.
-  void FramesToNextEventForTest(unsigned num_frames) {
-    frames_to_next_event_for_test_ = num_frames;
-  }
+  // To test event sampling. Controls whether we update the current sample
+  // on the next frame, or do not. Values persist until explicitly changed.
+  void ChooseNextFrameForTest();
+  void DoNotChooseNextFrameForTest();
 
-  // Used to check that we only for the MainFrame of a document.
+  // Used to check that we record only for the MainFrame of a document.
   bool AllMetricsAreZero();
 
   // The caller is the owner of the |clock|. The |clock| must outlive the
@@ -329,32 +343,36 @@
   Vector<AbsoluteMetricRecord> absolute_metric_records_;
   Vector<MainFramePercentageRecord> main_frame_percentage_records_;
 
-  // Sampling control. We use a Poisson process with an exponential decay
-  // multiplier. The goal is to get many randomly distributed samples early
-  // during page load and initial interaction, then samples at an exponentially
-  // decreasing rate to effectively cap the number of samples. The particular
-  // parameters chosen here give roughly 5-10 samples in the first 100 frames,
-  // decaying to several hours between samples by the 40th sample. The
-  // multiplier value and sample_decay_rate_ should be tuned to achieve a total
-  // sample count that avoids throttling by the UKM system.
-  double sample_decay_rate_ = 1;
-  double sample_rate_multiplier_ = 2;
-  unsigned samples_so_far_ = 0;
-  unsigned frames_to_next_event_ = 0;
+  // The current sample to report. When RecordEvent() is called we
+  // check for uniform_random[0,1) < 1 / n where n is the number of frames
+  // we have seen (including this one). If true, we replace the sample with
+  // the current frame data. The result is a uniformly randomly chosen frame
+  // in the period between the frame counter being reset and the recording
+  // to the UKM system of the current sample.
+  // This process is designed to get maximum utility while only sending 2
+  // events per page load, which in turn maximizes client counts.
+  SampleToRecord current_sample_;
+  unsigned frames_since_last_report_ = 0;
 
   // Control for the ForcedStyleAndUpdate UMA metric sampling
   unsigned mean_calls_between_forced_style_layout_uma_ = 100;
   unsigned calls_to_next_forced_style_layout_uma_ = 0;
 
-  // Test data, used for SampleFramesToNextEvent if present
-  unsigned frames_to_next_event_for_test_ = 0;
-
   // Set by BeginMainFrame() and cleared in RecordMEndOfFrameMetrics.
   // Main frame metrics are only recorded if this is true.
   bool in_main_frame_update_ = false;
 
-  // Record whether or not it is before the First Contentful Paint.
-  bool is_before_fcp_ = true;
+  // A bitfield maintaining state for first contentful paint.
+  enum FCPState { kBeforeFCPSignal, kThisFrameReachedFCP, kHavePassedFCP };
+  FCPState fcp_state_ = kBeforeFCPSignal;
+
+  // A bitfield used to control updating the sample for tests.
+  enum SampleControlForTest {
+    kNoPreference,
+    kMustChooseNextFrame,
+    kMustNotChooseNextFrame
+  };
+  SampleControlForTest next_frame_sample_control_for_test_ = kNoPreference;
 
   DISALLOW_COPY_AND_ASSIGN(LocalFrameUkmAggregator);
 };
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
index 325558e7..dbf50505 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
@@ -49,14 +49,9 @@
            "Percentage";
   }
 
-  void FramesToNextEventForTest(unsigned delta) {
-    aggregator().FramesToNextEventForTest(delta);
-  }
-
-  unsigned FramesToNextEvent() { return aggregator().frames_to_next_event_; }
-  unsigned SamplesSoFar() { return aggregator().samples_so_far_; }
-  unsigned SampleFramesToNextEvent() {
-    return aggregator().SampleFramesToNextEvent();
+  void ChooseNextFrameForTest() { aggregator().ChooseNextFrameForTest(); }
+  void DoNotChooseNextFrameForTest() {
+    aggregator().DoNotChooseNextFrameForTest();
   }
 
   base::TimeTicks Now() { return test_task_runner_->NowTicks(); }
@@ -64,43 +59,42 @@
  protected:
   scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
 
-  void VerifyUpdateEntries(unsigned expected_num_entries,
-                           unsigned expected_primary_metric,
-                           unsigned expected_sub_metric,
-                           unsigned expected_percentage,
-                           unsigned expected_reasons,
-                           bool expected_before_fcp) {
+  void VerifyUpdateEntry(unsigned index,
+                         unsigned expected_primary_metric,
+                         unsigned expected_sub_metric,
+                         unsigned expected_percentage,
+                         unsigned expected_reasons,
+                         bool expected_before_fcp) {
     auto entries = recorder().GetEntriesByName("Blink.UpdateTime");
-    EXPECT_EQ(entries.size(), expected_num_entries);
+    EXPECT_GT(entries.size(), index);
 
-    for (auto* entry : entries) {
+    auto* entry = entries[index];
+    EXPECT_TRUE(
+        ukm::TestUkmRecorder::EntryHasMetric(entry, GetPrimaryMetricName()));
+    const int64_t* primary_metric_value =
+        ukm::TestUkmRecorder::GetEntryMetric(entry, GetPrimaryMetricName());
+    EXPECT_NEAR(*primary_metric_value / 1e3, expected_primary_metric, 0.001);
+    for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
       EXPECT_TRUE(
-          ukm::TestUkmRecorder::EntryHasMetric(entry, GetPrimaryMetricName()));
-      const int64_t* primary_metric_value =
-          ukm::TestUkmRecorder::GetEntryMetric(entry, GetPrimaryMetricName());
-      EXPECT_NEAR(*primary_metric_value / 1e3, expected_primary_metric, 0.001);
-      for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
-        EXPECT_TRUE(
-            ukm::TestUkmRecorder::EntryHasMetric(entry, GetMetricName(i)));
-        const int64_t* metric_value =
-            ukm::TestUkmRecorder::GetEntryMetric(entry, GetMetricName(i));
-        EXPECT_NEAR(*metric_value / 1e3, expected_sub_metric, 0.001);
+          ukm::TestUkmRecorder::EntryHasMetric(entry, GetMetricName(i)));
+      const int64_t* metric_value =
+          ukm::TestUkmRecorder::GetEntryMetric(entry, GetMetricName(i));
+      EXPECT_NEAR(*metric_value / 1e3, expected_sub_metric, 0.001);
 
-        EXPECT_TRUE(ukm::TestUkmRecorder::EntryHasMetric(
-            entry, GetPercentageMetricName(i)));
-        const int64_t* metric_percentage = ukm::TestUkmRecorder::GetEntryMetric(
-            entry, GetPercentageMetricName(i));
-        EXPECT_NEAR(*metric_percentage, expected_percentage, 0.001);
-      }
-      EXPECT_TRUE(
-          ukm::TestUkmRecorder::EntryHasMetric(entry, "MainFrameIsBeforeFCP"));
-      EXPECT_EQ(expected_before_fcp, *ukm::TestUkmRecorder::GetEntryMetric(
-                                         entry, "MainFrameIsBeforeFCP"));
-      EXPECT_TRUE(
-          ukm::TestUkmRecorder::EntryHasMetric(entry, "MainFrameReasons"));
-      EXPECT_EQ(expected_reasons, *ukm::TestUkmRecorder::GetEntryMetric(
-                                      entry, "MainFrameReasons"));
+      EXPECT_TRUE(ukm::TestUkmRecorder::EntryHasMetric(
+          entry, GetPercentageMetricName(i)));
+      const int64_t* metric_percentage = ukm::TestUkmRecorder::GetEntryMetric(
+          entry, GetPercentageMetricName(i));
+      EXPECT_NEAR(*metric_percentage, expected_percentage, 0.001);
     }
+    EXPECT_TRUE(
+        ukm::TestUkmRecorder::EntryHasMetric(entry, "MainFrameIsBeforeFCP"));
+    EXPECT_EQ(expected_before_fcp, *ukm::TestUkmRecorder::GetEntryMetric(
+                                       entry, "MainFrameIsBeforeFCP"));
+    EXPECT_TRUE(
+        ukm::TestUkmRecorder::EntryHasMetric(entry, "MainFrameReasons"));
+    EXPECT_EQ(expected_reasons,
+              *ukm::TestUkmRecorder::GetEntryMetric(entry, "MainFrameReasons"));
   }
 
   void VerifyAggregatedEntries(unsigned expected_num_entries,
@@ -125,6 +119,27 @@
     }
   }
 
+  void SimulateFrame(base::TimeTicks start_time,
+                     unsigned millisecond_per_step,
+                     cc::ActiveFrameSequenceTrackers trackers,
+                     bool mark_fcp = false) {
+    aggregator().BeginMainFrame();
+    for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
+      auto timer = aggregator().GetScopedTimer(i);
+      if (mark_fcp && i == static_cast<int>(LocalFrameUkmAggregator::kPaint))
+        aggregator().DidReachFirstContentfulPaint(true);
+      test_task_runner_->FastForwardBy(
+          base::TimeDelta::FromMilliseconds(millisecond_per_step));
+    }
+    aggregator().RecordEndOfFrameMetrics(start_time, Now(), trackers);
+  }
+
+  bool SampleMatchesIteration(int64_t iteration_count) {
+    return aggregator()
+               .current_sample_.sub_metrics_durations[0]
+               .InMilliseconds() == iteration_count;
+  }
+
  private:
   scoped_refptr<LocalFrameUkmAggregator> aggregator_;
   ukm::TestUkmRecorder recorder_;
@@ -146,6 +161,8 @@
 }
 
 TEST_F(LocalFrameUkmAggregatorTest, FirstFrameIsRecorded) {
+  // Verifies that we always get a sample when we report at least one frame.
+
   // Although the tests use a mock clock, the UKM aggregator checks if the
   // system has a high resolution clock before recording results. As a result,
   // the tests will fail if the system does not have a high resolution clock.
@@ -155,17 +172,14 @@
   // The initial interval is always zero, so we should see one set of metrics
   // for the initial frame, regardless of the initial interval.
   base::TimeTicks start_time = Now();
-  FramesToNextEventForTest(1);
   unsigned millisecond_for_step = 1;
-  aggregator().BeginMainFrame();
-  for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
-    auto timer =
-        aggregator().GetScopedTimer(i % LocalFrameUkmAggregator::kCount);
-    test_task_runner_->FastForwardBy(
-        base::TimeDelta::FromMilliseconds(millisecond_for_step));
-  }
-  aggregator().RecordEndOfFrameMetrics(start_time, Now(), 0);
+  SimulateFrame(start_time, millisecond_for_step, 12);
 
+  // Metrics are not reported until destruction.
+  EXPECT_EQ(recorder().entries_count(), 0u);
+
+  // Reset the aggregator. Should record one pre-FCP metric.
+  ResetAggregator();
   EXPECT_EQ(recorder().entries_count(), 1u);
 
   float expected_primary_metric =
@@ -174,119 +188,60 @@
   float expected_percentage =
       floor(100.0 / static_cast<float>(LocalFrameUkmAggregator::kCount));
 
-  VerifyUpdateEntries(1u, expected_primary_metric, expected_sub_metric,
-                      expected_percentage, 0, true);
-
-  // Reset the aggregator. Should not record any more.
-  ResetAggregator();
-
-  VerifyUpdateEntries(1u, expected_primary_metric, expected_sub_metric,
-                      expected_percentage, 0, true);
+  VerifyUpdateEntry(0u, expected_primary_metric, expected_sub_metric,
+                    expected_percentage, 12, true);
 }
 
-TEST_F(LocalFrameUkmAggregatorTest, EventsRecordedAtIntervals) {
+TEST_F(LocalFrameUkmAggregatorTest, PreAndPostFCPAreRecorded) {
+  // Confirm that we get at least one frame pre-FCP and one post-FCP.
+
   // Although the tests use a mock clock, the UKM aggregator checks if the
   // system has a high resolution clock before recording results. As a result,
   // the tests will fail if the system does not have a high resolution clock.
   if (!base::TimeTicks::IsHighResolution())
     return;
 
-  // The records should be recorded in the first frame after every interval,
-  // and no sooner.
-
-  // If we claim we are past FCP, the event should indicate that.
-  aggregator().DidReachFirstContentfulPaint(true);
-
-  // Set the first sample interval to 2.
-  FramesToNextEventForTest(2);
-  unsigned millisecond_per_step = 50 / (LocalFrameUkmAggregator::kCount + 1);
-  unsigned millisecond_per_frame =
-      millisecond_per_step * (LocalFrameUkmAggregator::kCount + 1);
-
+  // The initial interval is always zero, so we should see one set of metrics
+  // for the initial frame, regardless of the initial interval.
   base::TimeTicks start_time = Now();
-  aggregator().BeginMainFrame();
-  for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
-    auto timer = aggregator().GetScopedTimer(i);
-    test_task_runner_->FastForwardBy(
-        base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  }
-  test_task_runner_->FastForwardBy(
-      base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  aggregator().RecordEndOfFrameMetrics(start_time, Now(), 4);
+  unsigned millisecond_per_step = 50 / (LocalFrameUkmAggregator::kCount + 1);
+  SimulateFrame(start_time, millisecond_per_step, 4, true);
+
+  // We marked FCP when we simulated, so we should report something. There
+  // should be 2 entries because the aggregated pre-FCP metric also reported.
+  EXPECT_EQ(recorder().entries_count(), 2u);
+
+  float expected_primary_metric =
+      millisecond_per_step * LocalFrameUkmAggregator::kCount;
+  float expected_sub_metric = millisecond_per_step;
+  float expected_percentage =
+      floor(100.0 / static_cast<float>(LocalFrameUkmAggregator::kCount));
+
+  VerifyUpdateEntry(0u, expected_primary_metric, expected_sub_metric,
+                    expected_percentage, 4, true);
+
+  // Take another step. Should reset the frame count and report the first post-
+  // fcp frame. A failure here iundicates that we did not reset the frame,
+  // or that we are incorrectly tracking pre/post fcp.
+  unsigned millisecond_per_frame =
+      millisecond_per_step * LocalFrameUkmAggregator::kCount;
+
+  start_time = Now();
+  SimulateFrame(start_time, millisecond_per_step, 4);
+
+  // Need to destruct to report
+  ResetAggregator();
 
   // We should have a sample after the very first step, regardless of the
   // interval. The FirstFrameIsRecorded test above also tests this. There
-  // should be 2 entries because the aggregated pre-fcp event has also
+  // should be 3 entries because the aggregated pre-fcp event has also
   // been recorded.
-  float expected_percentage = floor(millisecond_per_step * 100.0 /
-                                    static_cast<float>(millisecond_per_frame));
-  VerifyUpdateEntries(1u, millisecond_per_frame, millisecond_per_step,
-                      expected_percentage, 4, false);
+  EXPECT_EQ(recorder().entries_count(), 3u);
 
-  // Another step does not get us past the sample interval.
-  start_time = Now();
-  aggregator().BeginMainFrame();
-  for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
-    auto timer = aggregator().GetScopedTimer(i);
-    test_task_runner_->FastForwardBy(
-        base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  }
-  test_task_runner_->FastForwardBy(
-      base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  aggregator().RecordEndOfFrameMetrics(start_time, Now(), 8);
-
-  VerifyUpdateEntries(1u, millisecond_per_frame, millisecond_per_step,
-                      expected_percentage, 4, false);
-
-  // Another step should tick us past the sample interval.
-  // Note that the sample is a single frame, so even if we've taken
-  // multiple steps we should see just one frame's time.
-  start_time = Now();
-  aggregator().BeginMainFrame();
-  for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
-    auto timer = aggregator().GetScopedTimer(i);
-    test_task_runner_->FastForwardBy(
-        base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  }
-  test_task_runner_->FastForwardBy(
-      base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  aggregator().RecordEndOfFrameMetrics(start_time, Now(), 4);
-
-  VerifyUpdateEntries(2u, millisecond_per_frame, millisecond_per_step,
-                      expected_percentage, 4, false);
-
-  // Step one more frame so we don't sample again.
-  start_time = Now();
-  aggregator().BeginMainFrame();
-  for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
-    auto timer = aggregator().GetScopedTimer(i);
-    test_task_runner_->FastForwardBy(
-        base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  }
-  test_task_runner_->FastForwardBy(
-      base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  aggregator().RecordEndOfFrameMetrics(start_time, Now(), 32);
-
-  // Should be no more samples.
-  VerifyUpdateEntries(2u, millisecond_per_frame, millisecond_per_step,
-                      expected_percentage, 4, false);
-
-  // And one more step to generate one more sample
-  start_time = Now();
-  aggregator().BeginMainFrame();
-  for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
-    auto timer = aggregator().GetScopedTimer(i);
-    test_task_runner_->FastForwardBy(
-        base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  }
-  test_task_runner_->FastForwardBy(
-      base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  aggregator().RecordEndOfFrameMetrics(start_time, Now(), 4);
-
-  // We should have 3 more events, once for the prior interval and 2 for the
-  // new interval.
-  VerifyUpdateEntries(3u, millisecond_per_frame, millisecond_per_step,
-                      expected_percentage, 4, false);
+  expected_percentage = floor(millisecond_per_step * 100.0 /
+                              static_cast<float>(millisecond_per_frame));
+  VerifyUpdateEntry(1u, millisecond_per_frame, millisecond_per_step,
+                    expected_percentage, 4, false);
 }
 
 TEST_F(LocalFrameUkmAggregatorTest, AggregatedPreFCPEventRecorded) {
@@ -296,49 +251,30 @@
   if (!base::TimeTicks::IsHighResolution())
     return;
 
-  // Set the first sample interval to 5. We shouldn't need to record an
+  // Be sure to not choose the next frame. We shouldn't need to record an
   // UpdateTime metric in order to record an aggregated metric.
-  FramesToNextEventForTest(5);
+  DoNotChooseNextFrameForTest();
   unsigned millisecond_per_step = 50 / (LocalFrameUkmAggregator::kCount + 1);
   unsigned millisecond_per_frame =
-      millisecond_per_step * (LocalFrameUkmAggregator::kCount + 1);
+      millisecond_per_step * (LocalFrameUkmAggregator::kCount);
 
   base::TimeTicks start_time = Now();
-  aggregator().BeginMainFrame();
-  for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
-    auto timer = aggregator().GetScopedTimer(i);
-    test_task_runner_->FastForwardBy(
-        base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  }
-  test_task_runner_->FastForwardBy(
-      base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  aggregator().RecordEndOfFrameMetrics(start_time, Now(), 0);
+  SimulateFrame(start_time, millisecond_per_step, 3);
 
   // We should not have an aggregated metric yet because we have not reached
-  // FCP.
-  VerifyAggregatedEntries(0u, millisecond_per_frame, millisecond_per_step);
+  // FCP. We shouldn't have any other kind of metric either.
+  EXPECT_EQ(recorder().entries_count(), 0u);
 
-  // Another step does not get us past the sample interval.
+  // Another step marking FCP this time.
+  ChooseNextFrameForTest();
   start_time = Now();
-  aggregator().BeginMainFrame();
-  for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
-    auto timer = aggregator().GetScopedTimer(i);
-    test_task_runner_->FastForwardBy(
-        base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  }
-  test_task_runner_->FastForwardBy(
-      base::TimeDelta::FromMilliseconds(millisecond_per_step));
-  aggregator().RecordEndOfFrameMetrics(start_time, Now(), 0);
+  SimulateFrame(start_time, millisecond_per_step, 3, true);
 
-  // Still no aggregated record because we have not reached FCP.
-  VerifyAggregatedEntries(0u, millisecond_per_frame, millisecond_per_step);
-
-  // If we claim we are past FCP, the event should indicate that.
-  aggregator().DidReachFirstContentfulPaint(true);
-
-  // Now we should have an aggregated metric.
+  // Now we should have an aggregated metric, plus the pre-FCP update metric
+  EXPECT_EQ(recorder().entries_count(), 2u);
   VerifyAggregatedEntries(1u, 2 * millisecond_per_frame,
                           2 * millisecond_per_step);
+  ResetAggregator();
 }
 
 TEST_F(LocalFrameUkmAggregatorTest, LatencyDataIsPopulated) {
@@ -348,9 +284,8 @@
   if (!base::TimeTicks::IsHighResolution())
     return;
 
-  // The initial interval is always zero, so we should see one set of metrics
-  // for the initial frame, regardless of the initial interval.
-  FramesToNextEventForTest(1);
+  // We always record the first frame. Din't use the SimulateFrame method
+  // because we need to populate before the end of the frame.
   unsigned millisecond_for_step = 1;
   aggregator().BeginMainFrame();
   for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
@@ -360,7 +295,6 @@
         base::TimeDelta::FromMilliseconds(millisecond_for_step));
   }
 
-  // Need to populate before the end of the frame.
   std::unique_ptr<cc::BeginMainFrameMetrics> metrics_data =
       aggregator().GetBeginMainFrameMetrics();
   EXPECT_EQ(metrics_data->handle_input_events.InMillisecondsF(),
@@ -378,22 +312,28 @@
             millisecond_for_step);
   // Do not check the value in metrics_data.update_layers because it
   // is not set by the aggregator.
+  ResetAggregator();
 }
 
-TEST_F(LocalFrameUkmAggregatorTest, SampleFramesGoesToMaxUnsigned) {
-  // This will time out if the exponential decay in sample rate does not
-  // happen. It should not take too many iterations to reach maximum time
-  // between samples.
-  unsigned initial_sample_count = SamplesSoFar();
-  unsigned last_sample_count = initial_sample_count;
-  unsigned frames_to_next_event = FramesToNextEvent();
-  while (frames_to_next_event < UINT_MAX) {
-    frames_to_next_event = SampleFramesToNextEvent();
-    EXPECT_GT(frames_to_next_event, 0u);
-    EXPECT_EQ(last_sample_count + 1, SamplesSoFar());
-    last_sample_count++;
+TEST_F(LocalFrameUkmAggregatorTest, SampleDoesChange) {
+  // To write a test that the sample eventually changes we need to let it very
+  // occasionally time out or fail. We'll go up to 100,000 tries for an update,
+  // so this should not hit on average once every 100,000 test runs. One flake
+  // in 100,000 seems acceptable.
+
+  // Generate the first frame. We will look for a change from this frame.
+  unsigned millisecond_for_step = 1;
+  SimulateFrame(base::TimeTicks(), millisecond_for_step, 0);
+
+  unsigned iteration_count = 2;
+  bool new_sample = false;
+  while (iteration_count < 100000u && !new_sample) {
+    millisecond_for_step = iteration_count;
+    SimulateFrame(base::TimeTicks(), millisecond_for_step, 0);
+    new_sample = SampleMatchesIteration(static_cast<int64_t>(iteration_count));
+    ++iteration_count;
   }
-  EXPECT_NE(initial_sample_count, last_sample_count);
+  EXPECT_LT(iteration_count, 100000u);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index b9fdeeb..12ffa76 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -381,6 +381,11 @@
   Frame::ApplyFrameOwnerProperties(std::move(properties));
 }
 
+void RemoteFrame::EnforceInsecureRequestPolicy(
+    mojom::blink::InsecureRequestPolicy policy) {
+  SetInsecureRequestPolicy(policy);
+}
+
 void RemoteFrame::SetReplicatedOrigin(
     const scoped_refptr<const SecurityOrigin>& origin,
     bool is_potentially_trustworthy_unique_origin) {
diff --git a/third_party/blink/renderer/core/frame/remote_frame.h b/third_party/blink/renderer/core/frame/remote_frame.h
index 814a6fb..baddf4d 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.h
+++ b/third_party/blink/renderer/core/frame/remote_frame.h
@@ -103,6 +103,8 @@
   void EnforceInsecureNavigationsSet(const WTF::Vector<uint32_t>& set) override;
   void SetFrameOwnerProperties(
       mojom::blink::FrameOwnerPropertiesPtr properties) override;
+  void EnforceInsecureRequestPolicy(
+      mojom::blink::InsecureRequestPolicy policy) override;
   void SetReplicatedOrigin(
       const scoped_refptr<const SecurityOrigin>& origin,
       bool is_potentially_trustworthy_unique_origin) override;
diff --git a/third_party/blink/renderer/core/frame/settings_delegate.h b/third_party/blink/renderer/core/frame/settings_delegate.h
index 8919c3d5..1b0bdcf 100644
--- a/third_party/blink/renderer/core/frame/settings_delegate.h
+++ b/third_party/blink/renderer/core/frame/settings_delegate.h
@@ -72,7 +72,6 @@
     kColorSchemeChange,
     kSpatialNavigationChange,
     kUniversalAccessChange,
-    kVisionDeficiencyChange,
   };
 
   virtual void SettingsChanged(ChangeType) = 0;
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 7c0e72d..eb953393 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -1213,11 +1213,8 @@
   // Process the superclass first to ensure that `InActiveDocument()` is
   // updated.
   Element::InsertedInto(insertion_point);
+  HideNonce();
 
-  if (GetDocument().GetContentSecurityPolicy()->HasHeaderDeliveredPolicy() &&
-      InActiveDocument() && FastHasAttribute(html_names::kNonceAttr)) {
-    setAttribute(html_names::kNonceAttr, g_empty_atom);
-  }
   if (IsFormAssociatedCustomElement())
     EnsureElementInternals().InsertedInto(insertion_point);
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
index 70efd14..3d94b51 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
@@ -6,7 +6,6 @@
 
 #include "third_party/blink/public/common/input/web_touch_event.h"
 #include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/renderer/core/css/vision_deficiency.h"
 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
@@ -40,8 +39,6 @@
       max_touch_points_(&agent_state_, /*default_value=*/1),
       emulated_media_(&agent_state_, /*default_value=*/WTF::String()),
       emulated_media_features_(&agent_state_, /*default_value=*/WTF::String()),
-      emulated_vision_deficiency_(&agent_state_,
-                                  /*default_value=*/WTF::String()),
       navigator_platform_override_(&agent_state_,
                                    /*default_value=*/WTF::String()),
       user_agent_override_(&agent_state_, /*default_value=*/WTF::String()),
@@ -104,8 +101,6 @@
                             .build());
   }
   setEmulatedMedia(emulated_media_.Get(), std::move(features));
-  if (!emulated_vision_deficiency_.Get().IsNull())
-    setEmulatedVisionDeficiency(emulated_vision_deficiency_.Get());
   auto rgba = ParseRGBA(default_background_color_override_rgba_.Get());
   if (rgba)
     setDefaultBackgroundColorOverride(std::move(rgba));
@@ -171,8 +166,6 @@
   // (e.g. if we allowed two different front-ends with the same
   // settings to attach to the same page). TODO: support this use case.
   setEmulatedMedia(String(), {});
-  if (!emulated_vision_deficiency_.Get().IsNull())
-    setEmulatedVisionDeficiency(String("none"));
   setCPUThrottlingRate(1);
   setFocusEmulationEnabled(false);
   setDefaultBackgroundColorOverride(Maybe<protocol::DOM::RGBA>());
@@ -279,43 +272,6 @@
   return response;
 }
 
-Response InspectorEmulationAgent::setEmulatedVisionDeficiency(
-    const String& type) {
-  Response response = AssertPage();
-  if (!response.isSuccess())
-    return response;
-
-  VisionDeficiency vision_deficiency;
-  namespace TypeEnum =
-      protocol::Emulation::SetEmulatedVisionDeficiency::TypeEnum;
-  if (type == TypeEnum::None)
-    vision_deficiency = VisionDeficiency::kNoVisionDeficiency;
-  else if (type == TypeEnum::Achromatomaly)
-    vision_deficiency = VisionDeficiency::kAchromatomaly;
-  else if (type == TypeEnum::Achromatopsia)
-    vision_deficiency = VisionDeficiency::kAchromatopsia;
-  else if (type == TypeEnum::BlurredVision)
-    vision_deficiency = VisionDeficiency::kBlurredVision;
-  else if (type == TypeEnum::Deuteranomaly)
-    vision_deficiency = VisionDeficiency::kDeuteranomaly;
-  else if (type == TypeEnum::Deuteranopia)
-    vision_deficiency = VisionDeficiency::kDeuteranopia;
-  else if (type == TypeEnum::Protanomaly)
-    vision_deficiency = VisionDeficiency::kProtanomaly;
-  else if (type == TypeEnum::Protanopia)
-    vision_deficiency = VisionDeficiency::kProtanopia;
-  else if (type == TypeEnum::Tritanomaly)
-    vision_deficiency = VisionDeficiency::kTritanomaly;
-  else if (type == TypeEnum::Tritanopia)
-    vision_deficiency = VisionDeficiency::kTritanopia;
-  else
-    return Response::InvalidParams("Unknown vision deficiency type");
-
-  emulated_vision_deficiency_.Set(type);
-  GetWebViewImpl()->GetPage()->SetVisionDeficiency(vision_deficiency);
-  return response;
-}
-
 Response InspectorEmulationAgent::setCPUThrottlingRate(double rate) {
   Response response = AssertPage();
   if (!response.isSuccess())
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
index 4c77396..4543033e 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
@@ -48,7 +48,6 @@
       protocol::Maybe<String> media,
       protocol::Maybe<protocol::Array<protocol::Emulation::MediaFeature>>
           features) override;
-  protocol::Response setEmulatedVisionDeficiency(const String&) override;
   protocol::Response setCPUThrottlingRate(double) override;
   protocol::Response setFocusEmulationEnabled(bool) override;
   protocol::Response setVirtualTimePolicy(
@@ -128,7 +127,6 @@
   InspectorAgentState::Integer max_touch_points_;
   InspectorAgentState::String emulated_media_;
   InspectorAgentState::StringMap emulated_media_features_;
-  InspectorAgentState::String emulated_vision_deficiency_;
   InspectorAgentState::String navigator_platform_override_;
   InspectorAgentState::String user_agent_override_;
   InspectorAgentState::String accept_language_override_;
diff --git a/third_party/blink/renderer/core/inspector/inspector_protocol_config.json b/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
index b6ba2f3..bafa314 100644
--- a/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
+++ b/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
@@ -84,7 +84,7 @@
             {
                 "domain": "Emulation",
                 "include": ["forceViewport", "resetViewport", "resetPageScaleFactor", "setPageScaleFactor", "setScriptExecutionDisabled", "setTouchEmulationEnabled",
-                            "setEmulatedMedia", "setEmulatedVisionDeficiency", "setCPUThrottlingRate", "setVirtualTimePolicy", "setTimezoneOverride", "setNavigatorOverrides", "setDefaultBackgroundColorOverride", "setDeviceMetricsOverride", "clearDeviceMetricsOverride",
+                            "setEmulatedMedia", "setCPUThrottlingRate", "setVirtualTimePolicy", "setTimezoneOverride", "setNavigatorOverrides", "setDefaultBackgroundColorOverride", "setDeviceMetricsOverride", "clearDeviceMetricsOverride",
                             "setUserAgentOverride", "setScrollbarsHidden", "setDocumentCookieDisabled", "setFocusEmulationEnabled", "setLocaleOverride"],
                 "include_events": ["virtualTimeBudgetExpired", "virtualTimeAdvanced", "virtualTimePaused"]
             },
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index 8ff83ef..70905ed7 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -30,7 +30,6 @@
 #include "third_party/blink/renderer/core/css/media_feature_overrides.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
-#include "third_party/blink/renderer/core/css/vision_deficiency.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/visited_link_state.h"
 #include "third_party/blink/renderer/core/editing/drag_caret.h"
@@ -800,11 +799,6 @@
       }
       break;
     }
-    case SettingsDelegate::kVisionDeficiencyChange: {
-      if (auto* main_local_frame = DynamicTo<LocalFrame>(MainFrame()))
-        main_local_frame->GetDocument()->VisionDeficiencyChanged();
-      break;
-    }
   }
 }
 
@@ -1075,13 +1069,6 @@
   SettingsChanged(SettingsDelegate::kColorSchemeChange);
 }
 
-void Page::SetVisionDeficiency(VisionDeficiency new_vision_deficiency) {
-  if (new_vision_deficiency != vision_deficiency_) {
-    vision_deficiency_ = new_vision_deficiency;
-    SettingsChanged(SettingsDelegate::kVisionDeficiencyChange);
-  }
-}
-
 Page::PageClients::PageClients() : chrome_client(nullptr) {}
 
 template class CORE_TEMPLATE_EXPORT Supplement<Page>;
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index ee101e42..7f5b14a 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -31,7 +31,6 @@
 #include "third_party/blink/public/platform/web_text_autosizer_page_info.h"
 #include "third_party/blink/public/web/web_window_features.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/css/vision_deficiency.h"
 #include "third_party/blink/renderer/core/frame/deprecation.h"
 #include "third_party/blink/renderer/core/frame/hosts_using_features.h"
 #include "third_party/blink/renderer/core/frame/settings_delegate.h"
@@ -343,9 +342,6 @@
   }
   void ClearMediaFeatureOverrides();
 
-  void SetVisionDeficiency(VisionDeficiency new_vision_deficiency);
-  VisionDeficiency GetVisionDeficiency() const { return vision_deficiency_; }
-
   WebScopedVirtualTimePauser& HistoryNavigationVirtualTimePauser() {
     return history_navigation_virtual_time_pauser_;
   }
@@ -458,12 +454,9 @@
 
   std::unique_ptr<PageScheduler> page_scheduler_;
 
-  // Overrides for various media features, set from DevTools.
+  // Overrides for various media features set from the devtools.
   std::unique_ptr<MediaFeatureOverrides> media_feature_overrides_;
 
-  // Emulated vision deficiency, set from DevTools.
-  VisionDeficiency vision_deficiency_ = VisionDeficiency::kNoVisionDeficiency;
-
   int32_t autoplay_flags_;
 
   // Accessed by frames to determine whether to expose the PortalHost object.
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
index b4403c1..8d66f2a56 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
@@ -1441,7 +1441,10 @@
 
   // After an initial compositing update, we should have one scrolling update
   // recorded as PreFCP.
+  GetWebView()->MainFrameWidget()->RecordStartOfFrameMetrics();
   ForceFullCompositingUpdate();
+  GetWebView()->MainFrameWidget()->RecordEndOfFrameMetrics(base::TimeTicks(),
+                                                           0);
   histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 1);
   histogram_tester.ExpectTotalCount(
       "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
@@ -1451,7 +1454,10 @@
       "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 0);
 
   // An update with no scrolling changes should not cause a scrolling update.
+  GetWebView()->MainFrameWidget()->RecordStartOfFrameMetrics();
   ForceFullCompositingUpdate();
+  GetWebView()->MainFrameWidget()->RecordEndOfFrameMetrics(base::TimeTicks(),
+                                                           0);
   histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 1);
   histogram_tester.ExpectTotalCount(
       "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
@@ -1463,27 +1469,34 @@
   // A change to background color does not need to cause a scrolling update but,
   // because hit test display items paint, we also cause a scrolling coordinator
   // update when the background paints. Also render some text to get past FCP.
+  // Note that this frame is still considered pre-FCP.
   auto* background = GetFrame()->GetDocument()->getElementById("bg");
   background->removeAttribute(html_names::kStyleAttr);
   background->SetInnerHTMLFromString("Some Text");
+  GetWebView()->MainFrameWidget()->RecordStartOfFrameMetrics();
   ForceFullCompositingUpdate();
+  GetWebView()->MainFrameWidget()->RecordEndOfFrameMetrics(base::TimeTicks(),
+                                                           0);
   histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 2);
   histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
+      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 2);
   histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 1);
+      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
   histogram_tester.ExpectTotalCount(
       "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 1);
 
   // Removing a scrollable area should cause a scrolling update.
   auto* scroller = GetFrame()->GetDocument()->getElementById("scroller");
   scroller->removeAttribute(html_names::kStyleAttr);
+  GetWebView()->MainFrameWidget()->RecordStartOfFrameMetrics();
   ForceFullCompositingUpdate();
+  GetWebView()->MainFrameWidget()->RecordEndOfFrameMetrics(base::TimeTicks(),
+                                                           0);
   histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 3);
   histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
+      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 2);
   histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 2);
+      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 1);
   histogram_tester.ExpectTotalCount(
       "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 1);
 }
diff --git a/third_party/blink/renderer/core/style/OWNERS b/third_party/blink/renderer/core/style/OWNERS
index d1eb1c3..a007a39 100644
--- a/third_party/blink/renderer/core/style/OWNERS
+++ b/third_party/blink/renderer/core/style/OWNERS
@@ -2,6 +2,7 @@
 andruud@chromium.org
 ericwilligers@chromium.org
 futhark@chromium.org
+xiaochengh@chromium.org
 
 # TEAM: layout-dev@chromium.org
 # COMPONENT: Blink>CSS
diff --git a/third_party/blink/renderer/core/svg/svg_element.cc b/third_party/blink/renderer/core/svg/svg_element.cc
index c2b425c..1bf5161 100644
--- a/third_party/blink/renderer/core/svg/svg_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_element.cc
@@ -296,16 +296,8 @@
 Node::InsertionNotificationRequest SVGElement::InsertedInto(
     ContainerNode& root_parent) {
   Element::InsertedInto(root_parent);
+  HideNonce();
   UpdateRelativeLengthsInformation();
-
-  const AtomicString& nonce_value = FastGetAttribute(html_names::kNonceAttr);
-  if (!nonce_value.IsEmpty()) {
-    setNonce(nonce_value);
-    if (InActiveDocument() &&
-        GetDocument().GetContentSecurityPolicy()->HasHeaderDeliveredPolicy()) {
-      setAttribute(html_names::kNonceAttr, g_empty_atom);
-    }
-  }
   return kInsertionDone;
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_resource.cc b/third_party/blink/renderer/core/svg/svg_resource.cc
index 9cf40b6..213d359 100644
--- a/third_party/blink/renderer/core/svg/svg_resource.cc
+++ b/third_party/blink/renderer/core/svg/svg_resource.cc
@@ -139,22 +139,7 @@
   options.initiator_info.name = fetch_initiator_type_names::kCSS;
   FetchParameters params(ResourceRequest(url_), options);
   params.MutableResourceRequest().SetMode(
-      network::mojom::blink::RequestMode::kSameOrigin);
-  resource_document_ =
-      DocumentResource::FetchSVGDocument(params, document.Fetcher(), this);
-  target_ = ResolveTarget();
-}
-
-void ExternalSVGResource::LoadWithoutCSP(const Document& document) {
-  if (resource_document_)
-    return;
-  ResourceLoaderOptions options;
-  options.initiator_info.name = fetch_initiator_type_names::kCSS;
-  FetchParameters params(ResourceRequest(url_), options);
-  params.SetContentSecurityCheck(
-      network::mojom::blink::CSPDisposition::DO_NOT_CHECK);
-  params.MutableResourceRequest().SetMode(
-      network::mojom::blink::RequestMode::kSameOrigin);
+      network::mojom::RequestMode::kSameOrigin);
   resource_document_ =
       DocumentResource::FetchSVGDocument(params, document.Fetcher(), this);
   target_ = ResolveTarget();
diff --git a/third_party/blink/renderer/core/svg/svg_resource.h b/third_party/blink/renderer/core/svg/svg_resource.h
index aab5fd2..2272dda 100644
--- a/third_party/blink/renderer/core/svg/svg_resource.h
+++ b/third_party/blink/renderer/core/svg/svg_resource.h
@@ -64,7 +64,6 @@
   virtual ~SVGResource();
 
   virtual void Load(const Document&) {}
-  virtual void LoadWithoutCSP(const Document&) {}
 
   Element* Target() const { return target_; }
   LayoutSVGResourceContainer* ResourceContainer() const;
@@ -118,7 +117,6 @@
   explicit ExternalSVGResource(const KURL&);
 
   void Load(const Document&) override;
-  void LoadWithoutCSP(const Document&) override;
 
   void Trace(Visitor*) override;
 
diff --git a/third_party/blink/renderer/platform/graphics/OWNERS b/third_party/blink/renderer/platform/graphics/OWNERS
index 73a6535..0bb32866 100644
--- a/third_party/blink/renderer/platform/graphics/OWNERS
+++ b/third_party/blink/renderer/platform/graphics/OWNERS
@@ -13,6 +13,7 @@
 
 # For surface ID propagation and synchronization
 samans@chromium.org
+jonross@chromium.org
 
 # lowLatency canvas
 mcasas@chromium.org
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn
index a3ff491..3caa75a 100644
--- a/third_party/blink/renderer/platform/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -89,6 +89,8 @@
     "main_thread/main_thread_task_queue.h",
     "main_thread/memory_purge_manager.cc",
     "main_thread/memory_purge_manager.h",
+    "main_thread/non_waking_time_domain.cc",
+    "main_thread/non_waking_time_domain.h",
     "main_thread/page_scheduler_impl.cc",
     "main_thread/page_scheduler_impl.h",
     "main_thread/page_visibility_state.cc",
diff --git a/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc b/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
index e685d5dd..c456a69 100644
--- a/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
@@ -175,6 +175,10 @@
     return base::ThreadTaskRunnerHandle::Get();
   }
 
+  scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override {
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
   std::unique_ptr<PageScheduler> CreatePageScheduler(
       PageScheduler::Delegate*) override {
     return std::make_unique<DummyPageScheduler>();
diff --git a/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.cc
index 920153dc..97dbeaa 100644
--- a/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.cc
@@ -61,6 +61,11 @@
   return base::ThreadTaskRunnerHandle::Get();
 }
 
+scoped_refptr<base::SingleThreadTaskRunner>
+SimpleThreadScheduler::NonWakingTaskRunner() {
+  return base::ThreadTaskRunnerHandle::Get();
+}
+
 std::unique_ptr<PageScheduler> SimpleThreadScheduler::CreatePageScheduler(
     PageScheduler::Delegate* delegate) {
   return nullptr;
diff --git a/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.h
index 35c83b2..1111310 100644
--- a/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.h
@@ -55,6 +55,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override;
+  scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
       override;
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 222c1ee..edd6883 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -436,6 +436,7 @@
     case TaskType::kWorkerThreadTaskQueueDefault:
     case TaskType::kWorkerThreadTaskQueueV8:
     case TaskType::kWorkerThreadTaskQueueCompositor:
+    case TaskType::kMainThreadTaskQueueNonWaking:
     // The web scheduling API task types are used by WebSchedulingTaskQueues.
     // The associated TaskRunner should be obtained by creating a
     // WebSchedulingTaskQueue with CreateWebSchedulingTaskQueue().
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 5ebcf94..e3a5cd6b 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -216,6 +216,7 @@
                   TaskQueue::QueuePriority::kBestEffortPriority))),
       memory_purge_manager_(memory_purge_task_queue_->CreateTaskRunner(
           TaskType::kMainThreadTaskQueueMemoryPurge)),
+      non_waking_time_domain_(tick_clock()),
       delayed_update_policy_runner_(
           base::BindRepeating(&MainThreadSchedulerImpl::UpdatePolicy,
                               base::Unretained(this)),
@@ -237,12 +238,18 @@
   task_runners_.emplace(compositor_task_queue_,
                         compositor_task_queue_->CreateQueueEnabledVoter());
 
+  RegisterTimeDomain(&non_waking_time_domain_);
+
   v8_task_queue_ = NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
       MainThreadTaskQueue::QueueType::kV8));
   ipc_task_queue_ = NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
       MainThreadTaskQueue::QueueType::kIPC));
   cleanup_task_queue_ = NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
       MainThreadTaskQueue::QueueType::kCleanup));
+  non_waking_task_queue_ =
+      NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+                       MainThreadTaskQueue::QueueType::kNonWaking)
+                       .SetTimeDomain(&non_waking_time_domain_));
 
   v8_task_runner_ =
       v8_task_queue_->CreateTaskRunner(TaskType::kMainThreadTaskQueueV8);
@@ -254,6 +261,8 @@
       ipc_task_queue_->CreateTaskRunner(TaskType::kMainThreadTaskQueueIPC);
   cleanup_task_runner_ = cleanup_task_queue_->CreateTaskRunner(
       TaskType::kMainThreadTaskQueueCleanup);
+  non_waking_task_runner_ = non_waking_task_queue_->CreateTaskRunner(
+      TaskType::kMainThreadTaskQueueNonWaking);
 
   // TaskQueueThrottler requires some task runners, then initialize
   // TaskQueueThrottler after task queues/runners are initialized.
@@ -305,6 +314,8 @@
     pair.first->ShutdownTaskQueue();
   }
 
+  UnregisterTimeDomain(&non_waking_time_domain_);
+
   if (virtual_time_domain_)
     UnregisterTimeDomain(virtual_time_domain_.get());
 
@@ -2324,6 +2335,11 @@
   return compositor_task_runner_;
 }
 
+scoped_refptr<base::SingleThreadTaskRunner>
+MainThreadSchedulerImpl::NonWakingTaskRunner() {
+  return non_waking_task_runner_;
+}
+
 std::unique_ptr<PageScheduler> MainThreadSchedulerImpl::CreatePageScheduler(
     PageScheduler::Delegate* delegate) {
   return std::make_unique<PageSchedulerImpl>(delegate, this);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index f275b2bd..5efc92d 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -37,6 +37,7 @@
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h"
@@ -163,6 +164,7 @@
   // Note: this is also shared by the ThreadScheduler interface.
   scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> CleanupTaskRunner() override;
+  scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
       override;
   std::unique_ptr<WebRenderWidgetSchedulingState>
@@ -808,17 +810,20 @@
   scoped_refptr<MainThreadTaskQueue> ipc_task_queue_;
   scoped_refptr<MainThreadTaskQueue> cleanup_task_queue_;
   scoped_refptr<MainThreadTaskQueue> memory_purge_task_queue_;
+  scoped_refptr<MainThreadTaskQueue> non_waking_task_queue_;
 
   scoped_refptr<base::SingleThreadTaskRunner> v8_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> control_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> cleanup_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> non_waking_task_runner_;
 
   MemoryPurgeManager memory_purge_manager_;
 
   // Note |virtual_time_domain_| is lazily created.
   std::unique_ptr<AutoAdvancingVirtualTimeDomain> virtual_time_domain_;
+  NonWakingTimeDomain non_waking_time_domain_;
 
   base::RepeatingClosure update_policy_closure_;
   DeadlineTaskRunner delayed_update_policy_runner_;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index 474b338..5e7e3d8 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -3538,6 +3538,52 @@
   EXPECT_FALSE(scheduler_->IsBeginMainFrameScheduled());
 }
 
+TEST_F(MainThreadSchedulerImplTest, NonWakingTaskQueue) {
+  std::vector<std::pair<std::string, base::TimeTicks>> log;
+  base::TimeTicks start = scheduler_->GetTickClock()->NowTicks();
+
+  scheduler_->DefaultTaskQueue()->task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](std::vector<std::pair<std::string, base::TimeTicks>>* log,
+             const base::TickClock* clock) {
+            log->emplace_back("regular (immediate)", clock->NowTicks());
+          },
+          &log, scheduler_->GetTickClock()));
+  scheduler_->NonWakingTaskRunner()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](std::vector<std::pair<std::string, base::TimeTicks>>* log,
+             const base::TickClock* clock) {
+            log->emplace_back("non-waking", clock->NowTicks());
+          },
+          &log, scheduler_->GetTickClock()),
+      base::TimeDelta::FromSeconds(3));
+  scheduler_->DefaultTaskQueue()->task_runner()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](std::vector<std::pair<std::string, base::TimeTicks>>* log,
+             const base::TickClock* clock) {
+            log->emplace_back("regular (delayed)", clock->NowTicks());
+          },
+          &log, scheduler_->GetTickClock()),
+      base::TimeDelta::FromSeconds(5));
+
+  test_task_runner_->FastForwardUntilNoTasksRemain();
+
+  // Check that the non-waking task runner didn't generate an unnecessary
+  // wake-up.
+  // Note: the exact order of these tasks is not fixed and depends on the time
+  // domain iteration order.
+  EXPECT_THAT(
+      log,
+      testing::UnorderedElementsAre(
+          std::make_pair("regular (immediate)", start),
+          std::make_pair("non-waking", start + base::TimeDelta::FromSeconds(5)),
+          std::make_pair("regular (delayed)",
+                         start + base::TimeDelta::FromSeconds(5))));
+}
+
 class VeryHighPriorityForCompositingAlwaysExperimentTest
     : public MainThreadSchedulerImplTest {
  public:
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
index d008643..0d54d31 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
@@ -63,6 +63,8 @@
       return "other_tq";
     case MainThreadTaskQueue::QueueType::kWebScheduling:
       return "web_scheduling_tq";
+    case MainThreadTaskQueue::QueueType::kNonWaking:
+      return "non_waking_tq";
     case MainThreadTaskQueue::QueueType::kCount:
       NOTREACHED();
       return nullptr;
@@ -95,6 +97,7 @@
     case MainThreadTaskQueue::QueueType::kInput:
     case MainThreadTaskQueue::QueueType::kDetached:
     case MainThreadTaskQueue::QueueType::kCleanup:
+    case MainThreadTaskQueue::QueueType::kNonWaking:
     case MainThreadTaskQueue::QueueType::kOther:
       return false;
     case MainThreadTaskQueue::QueueType::kCount:
@@ -114,6 +117,7 @@
     case QueueType::kTest:
     case QueueType::kV8:
     case QueueType::kIPC:
+    case QueueType::kNonWaking:
     case QueueType::kCleanup:
       return QueueClass::kNone;
     case QueueType::kFrameLoading:
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
index c1ea93cd..4329479 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
@@ -76,10 +76,11 @@
     // 22 : kWebSchedulingBestEffort, obsolete.
 
     kWebScheduling = 24,
+    kNonWaking = 25,
 
     // Used to group multiple types when calculating Expected Queueing Time.
     kOther = 23,
-    kCount = 25
+    kCount = 26
   };
 
   // Returns name of the given queue type. Returned string has application
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.cc b/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.cc
new file mode 100644
index 0000000..13d6f4a9
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.cc
@@ -0,0 +1,45 @@
+// 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 "third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.h"
+
+namespace blink {
+namespace scheduler {
+
+NonWakingTimeDomain::NonWakingTimeDomain(const base::TickClock* tick_clock)
+    : tick_clock_(tick_clock) {}
+
+NonWakingTimeDomain::~NonWakingTimeDomain() = default;
+
+base::sequence_manager::LazyNow NonWakingTimeDomain::CreateLazyNow() const {
+  return base::sequence_manager::LazyNow(tick_clock_);
+}
+
+base::TimeTicks NonWakingTimeDomain::Now() const {
+  return tick_clock_->NowTicks();
+}
+
+base::Optional<base::TimeDelta> NonWakingTimeDomain::DelayTillNextTask(
+    base::sequence_manager::LazyNow* lazy_now) {
+  // NonWakingTimeDomain should never generate wakeups on its own.
+  return base::nullopt;
+}
+
+bool NonWakingTimeDomain::MaybeFastForwardToNextTask(
+    bool quit_when_idle_requested) {
+  return false;
+}
+
+const char* NonWakingTimeDomain::GetName() const {
+  return "non_waking_time_domain";
+}
+
+void NonWakingTimeDomain::SetNextDelayedDoWork(
+    base::sequence_manager::LazyNow* lazy_now,
+    base::TimeTicks run_time) {
+  // Do not request a wake-up, unlike a regular TimeDomain.
+}
+
+}  // namespace scheduler
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.h b/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.h
new file mode 100644
index 0000000..9641ed9
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.h
@@ -0,0 +1,38 @@
+// 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_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_NON_WAKING_TIME_DOMAIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_NON_WAKING_TIME_DOMAIN_H_
+
+#include "base/task/sequence_manager/time_domain.h"
+#include "base/time/tick_clock.h"
+
+namespace blink {
+namespace scheduler {
+
+// A time domain which never generates wake-ups on its own. Useful for tasks
+// which should run only when the system is non-idle.
+class NonWakingTimeDomain : public base::sequence_manager::TimeDomain {
+ public:
+  explicit NonWakingTimeDomain(const base::TickClock* tick_clock);
+  ~NonWakingTimeDomain() override;
+
+  // TimeDomain:
+  base::sequence_manager::LazyNow CreateLazyNow() const override;
+  base::TimeTicks Now() const override;
+  base::Optional<base::TimeDelta> DelayTillNextTask(
+      base::sequence_manager::LazyNow* lazy_now) override;
+  bool MaybeFastForwardToNextTask(bool quit_when_idle_requested) override;
+  const char* GetName() const override;
+  void SetNextDelayedDoWork(base::sequence_manager::LazyNow* lazy_now,
+                            base::TimeTicks run_time) override;
+
+ private:
+  const base::TickClock* tick_clock_;
+};
+
+}  // namespace scheduler
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_NON_WAKING_TIME_DOMAIN_H_
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
index 46cbd2b..52ffc2cb 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
@@ -107,6 +107,8 @@
       return "MainThreadTaskQueueCleanup";
     case TaskType::kMainThreadTaskQueueMemoryPurge:
       return "MainThreadTaskQueueMemoryPurge";
+    case TaskType::kMainThreadTaskQueueNonWaking:
+      return "MainThreadTaskQueueNonWaking";
     case TaskType::kInternalIntersectionObserver:
       return "InternalIntersectionObserver";
     case TaskType::kCompositorThreadTaskQueueDefault:
diff --git a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
index 0f1eddf..20e3e743 100644
--- a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
@@ -89,6 +89,11 @@
   // Returns a task runner for kV8 tasks. Can be called from any thread.
   virtual scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() = 0;
 
+  // Returns a task runner which does not generate system wakeups on its own.
+  // This means that if a delayed task is posted to it, it will run when
+  // the delay expires AND another task runs.
+  virtual scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() = 0;
+
   // Returns a task runner for compositor tasks. This is intended only to be
   // used by specific animation and rendering related tasks (e.g. animated GIFS)
   // and should not generally be used.
diff --git a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc
index 9580c0b..0bc371a 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc
@@ -93,6 +93,12 @@
   return nullptr;
 }
 
+scoped_refptr<base::SingleThreadTaskRunner>
+CompositorThreadScheduler::NonWakingTaskRunner() {
+  NOTREACHED();
+  return nullptr;
+}
+
 bool CompositorThreadScheduler::CanExceedIdleDeadlineIfRequired() const {
   return false;
 }
diff --git a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h
index 8dc3759..3c808170 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h
@@ -43,6 +43,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override;
+  scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override;
   bool ShouldYieldForHighPriorityWork() override;
   bool CanExceedIdleDeadlineIfRequired() const override;
   void AddTaskObserver(base::TaskObserver* task_observer) override;
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
index b9a44ecc..5de97638 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
@@ -197,6 +197,7 @@
     case TaskType::kMainThreadTaskQueueControl:
     case TaskType::kMainThreadTaskQueueCleanup:
     case TaskType::kMainThreadTaskQueueMemoryPurge:
+    case TaskType::kMainThreadTaskQueueNonWaking:
     case TaskType::kCompositorThreadTaskQueueDefault:
     case TaskType::kCompositorThreadTaskQueueInput:
     case TaskType::kWorkerThreadTaskQueueDefault:
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
index 6a202f8..4434805 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
@@ -155,6 +155,12 @@
   return nullptr;
 }
 
+scoped_refptr<base::SingleThreadTaskRunner>
+WorkerThreadScheduler::NonWakingTaskRunner() {
+  NOTREACHED() << "Not implemented";
+  return nullptr;
+}
+
 bool WorkerThreadScheduler::CanExceedIdleDeadlineIfRequired() const {
   DCHECK(initialized_);
   return idle_helper_.CanExceedIdleDeadlineIfRequired();
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
index fb26754..130a125 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
@@ -50,6 +50,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override;
+  scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override;
   bool ShouldYieldForHighPriorityWork() override;
   bool CanExceedIdleDeadlineIfRequired() const override;
   void AddTaskObserver(base::TaskObserver* task_observer) override;
diff --git a/third_party/blink/renderer/platform/wtf/vector.h b/third_party/blink/renderer/platform/wtf/vector.h
index 7f3573e..f43d3c30 100644
--- a/third_party/blink/renderer/platform/wtf/vector.h
+++ b/third_party/blink/renderer/platform/wtf/vector.h
@@ -2017,7 +2017,6 @@
         Allocator::template Trace<T, VectorTraits<T>>(
             visitor, *const_cast<T*>(buffer_entry));
       }
-      CheckUnusedSlots(Buffer() + size(), Buffer() + capacity());
     }
   }
 }
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py b/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py
index 51056c1..2a95be7 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py
@@ -125,7 +125,8 @@
         self._process.kill()
 
 class _TargetHost(object):
-    def __init__(self, build_path, ports_to_forward, target_device):
+    def __init__(self, build_path, ports_to_forward, target_device,
+                 results_directory):
         try:
             self._target = None
             target_args = {
@@ -146,12 +147,12 @@
                 })
                 self._target = aemu_target.AemuTarget(**target_args)
             self._target.Start()
-            self._setup_target(build_path, ports_to_forward)
+            self._setup_target(build_path, ports_to_forward, results_directory)
         except:
             self.cleanup()
             raise
 
-    def _setup_target(self, build_path, ports_to_forward):
+    def _setup_target(self, build_path, ports_to_forward, results_directory):
         # Tell SSH to forward all server ports from the Fuchsia device to
         # the host.
         forwarding_flags = [
@@ -165,6 +166,12 @@
                                                    ssh_args=forwarding_flags,
                                                    stderr=subprocess.PIPE)
 
+        listener_log_path = os.path.join(results_directory, 'system.log')
+        listener_log = open(listener_log_path,'w')
+        self._listener = self._target.RunCommandPiped(['log_listener'],
+                                                    stderr=listener_log,
+                                                    stdout=listener_log)
+
         package_path = os.path.join(build_path, CONTENT_SHELL_PACKAGE_PATH)
         self._target.InstallPackage([package_path])
 
@@ -228,7 +235,8 @@
         super(FuchsiaPort, self).setup_test_run()
         try:
             self._target_host = _TargetHost(
-                self._build_path(), self.SERVER_PORTS, self._target_device)
+                self._build_path(), self.SERVER_PORTS, self._target_device,
+                self.results_directory())
 
             if self.get_option('zircon_logging'):
                 self._zircon_logger = SubprocessOutputLogger(
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index e35a693..7bc696b 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5209,22 +5209,6 @@
 # Utility for manual testing, not intended to be run as part of layout tests.
 crbug.com/785955 http/tests/credentialmanager/tools/virtual-authenticator-environment-manual.html [ Skip ]
 
-# These Web Authentication API would require either real hardware, or
-# virtual simulation thereof (work in progress, see: https://crbug.com/785955).
-# Sheriff 2018-08-16
-crbug.com/826936 external/wpt/webauthn/createcredential-badargs-authnrselection.https.html [ Pass Timeout Failure ]
-crbug.com/826936 external/wpt/webauthn/createcredential-badargs-rp.https.html [ Pass Timeout Failure ]
-crbug.com/826936 external/wpt/webauthn/createcredential-badargs-user.https.html [ Pass Timeout Failure ]
-crbug.com/826936 external/wpt/webauthn/createcredential-excludecredentials.https.html [ Pass Timeout Failure ]
-crbug.com/826936 external/wpt/webauthn/createcredential-passing.https.html [ Pass Timeout Failure ]
-crbug.com/826936 external/wpt/webauthn/createcredential-badargs-challenge.https.html [ Pass Timeout Failure ]
-crbug.com/826936 external/wpt/webauthn/createcredential-pubkeycredparams.https.html [ Pass Timeout Failure ]
-crbug.com/826936 external/wpt/webauthn/getcredential-badargs-rpid.https.html [ Pass Timeout Failure ]
-crbug.com/826936 external/wpt/webauthn/getcredential-badargs-userverification.https.html [ Pass Timeout Failure ]
-crbug.com/826936 external/wpt/webauthn/getcredential-extensions.https.html [ Pass Timeout Failure ]
-crbug.com/826936 external/wpt/webauthn/getcredential-passing.https.html [ Pass Timeout Failure ]
-crbug.com/826936 external/wpt/webauthn/getcredential-timeout.https.html [ Pass Timeout Failure ]
-
 # Sheriff 2018-04-11
 crbug.com/831796 fast/events/autoscroll-in-textfield.html [ Failure Pass ]
 crbug.com/831673 http/tests/devtools/reveal-objects.js [ Pass Timeout ]
@@ -6764,3 +6748,6 @@
 
 # Sheriff 2020-03-04
 crbug.com/1058244 [ Mac ] virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/scrollbar-rtl-manipulation.html [ Pass Failure ]
+
+# Ecosystem-Infra Sheriff 2020-03-04
+crbug.com/1058403 external/wpt/webaudio/the-audio-api/the-audioworklet-interface/suspended-context-messageport.https.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/nonce-hiding/nonces.html b/third_party/blink/web_tests/external/wpt/content-security-policy/nonce-hiding/nonces.html
index b023d060..7ee10a7 100644
--- a/third_party/blink/web_tests/external/wpt/content-security-policy/nonce-hiding/nonces.html
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/nonce-hiding/nonces.html
@@ -3,30 +3,62 @@
 <script src="/resources/testharnessreport.js"></script>
 <div id=log></div>
 <script>
-[["meh", ""],
- ["div", ""],
- ["script", ""],
- ["meh", "http://www.w3.org/2000/svg"],
- ["svg", "http://www.w3.org/2000/svg"],
- ["script", "http://www.w3.org/2000/svg"]].forEach(([localName, namespace]) => {
-  test(t => {
-    const element = namespace === "" ? document.createElement(localName) : document.createElementNS(namespace, localName);
-    t.add_cleanup(() => element.remove());
-    assert_equals(element.nonce, "", "Initial IDL attribute value");
-    element.setAttribute("nonce", "x");
-    assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");
-    assert_equals(element.getAttribute("nonce"), "x", "Content attribute is modified after content attribute set");
-    document.body.appendChild(element);
-    assert_equals(element.nonce, "x", "IDL attribute is unchanged after element insertion");
-    assert_equals(element.getAttribute("nonce"), "", "Content attribute is changed after element insertion");
-  }, `Basic nonce tests for ${localName} in ${namespace === "" ? "HTML" : "SVG"} namespace`);
+  const namespace_url= {
+    "HTML": "http://www.w3.org/1999/xhtml",
+    "SVG": "http://www.w3.org/2000/svg",
+  }
+  const test_cases = [
+    ["meh"    , "HTML"],
+    ["div"    , "HTML"],
+    ["script" , "HTML"],
+    ["meh"    , "SVG"],
+    ["svg"    , "SVG"],
+    ["script" , "SVG"],
+  ];
 
-  test(t => {
-    const element = namespace === "" ? document.createElement(localName) : document.createElementNS(namespace, localName);
-    element.setAttribute("nonce", "x");
-    assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");
-    element.removeAttribute("nonce");
-    assert_equals(element.nonce, "", "IDL attribute is empty after content attribute removal");
-  }, `Ensure that removal of content attribute does not affect IDL attribute for ${localName} in ${namespace === "" ? "HTML" : "SVG"} namespace`);
-});
+  test_cases.forEach(([localName, namespace]) => {
+    test(t => {
+      const element = document.createElementNS(namespace_url[namespace], localName);
+      t.add_cleanup(() => element.remove());
+      assert_equals(element.nonce, "", "Initial IDL attribute value");
+      assert_equals(element.getAttribute("nonce"), null, "Initial content attribute");
+
+      element.setAttribute("nonce", "x");
+      assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");
+      assert_equals(element.getAttribute("nonce"), "x", "Content attribute is modified after content attribute set");
+
+      document.body.appendChild(element);
+      assert_equals(element.nonce, "x", "IDL attribute is unchanged after element insertion");
+      assert_equals(element.getAttribute("nonce"), "", "Content attribute is changed after element insertion");
+    }, `Basic nonce tests for ${localName} in ${namespace} namespace`);
+
+    test(t => {
+      const element = document.createElementNS(namespace_url[namespace], localName);
+      t.add_cleanup(() => element.remove());
+      element.setAttribute("nonce", "x");
+      assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");
+
+      element.removeAttribute("nonce");
+      assert_equals(element.nonce, "", "IDL attribute is empty after content attribute removal");
+    }, `Ensure that removal of content attribute does not affect IDL attribute for ${localName} in ${namespace} namespace`);
+
+    test(t => {
+      const element = document.createElementNS(namespace_url[namespace], localName);
+      t.add_cleanup(() => element.remove());
+      assert_equals(element.nonce, "");
+      assert_equals(element.getAttribute("nonce"), null);
+
+      element.setAttribute("nonce", "");
+      assert_equals(element.nonce, "");
+      assert_equals(element.getAttribute("nonce"), "");
+
+      document.body.appendChild(element);
+      assert_equals(element.nonce, "");
+      assert_equals(element.getAttribute("nonce"), "");
+
+      element.removeAttribute("nonce");
+      assert_equals(element.nonce, "");
+      assert_equals(element.getAttribute("nonce"), null);
+    }, `Test empty nonces for ${localName} in ${namespace} namespace`);
+  });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-authnrselection.https-expected.txt b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-authnrselection.https-expected.txt
deleted file mode 100644
index c10a465..0000000
--- a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-authnrselection.https-expected.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-This is a testharness.js-based test.
-PASS Set up the test environment
-FAIL Bad AuthenticatorSelectionCriteria: authenticatorSelection is empty array assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-FAIL Bad AuthenticatorSelectionCriteria: authenticatorSelection is null assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection is empty string
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection is string
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection attachment is empty string
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection attachment is empty object
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection attachment is null
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection attachment platform
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection residentKey true
-FAIL Bad AuthenticatorSelectionCriteria: authenticatorSelection residentKey is string promise_rejects_js: Expected bad parameters to fail function "function() { throw e }" threw object "NotAllowedError: The operation either timed out or was not allowed. See: https://w3c.github.io/webauthn/#sec-assertion-privacy." ("NotAllowedError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification empty string
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification empty object
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification bad value
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification null
-PASS Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification required
-PASS Clean up the test environment
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-authnrselection.https.html b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-authnrselection.https.html
index 5da0745..9497a001 100644
--- a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-authnrselection.https.html
+++ b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-authnrselection.https.html
@@ -46,8 +46,6 @@
     authnrSelBadUvNull.userVerification = null;
 
     // authenticatorSelection bad values
-    new CreateCredentialsTest("options.publicKey.authenticatorSelection", []).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is empty array", TypeError);
-    new CreateCredentialsTest("options.publicKey.authenticatorSelection", null).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is null", TypeError);
     new CreateCredentialsTest("options.publicKey.authenticatorSelection", "").runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is empty string", TypeError);
     new CreateCredentialsTest("options.publicKey.authenticatorSelection", "none").runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is string", TypeError);
 
@@ -65,10 +63,6 @@
     new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelRkTrue)
       .modify("options.publicKey.timeout", 300)
       .runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection residentKey true", "NotAllowedError");
-    new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelRkBadString)
-      .modify("options.publicKey.timeout", 300)
-      .runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection residentKey is string", TypeError);
-    // TODO: not sure if rk is "boolean" or "truthy"; add test cases if it should only accept boolean values
 
     // authenticatorSelection bad userVerification values
     new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelBadUvEmptyStr).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification empty string", TypeError);
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-rp.https-expected.txt b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-rp.https-expected.txt
deleted file mode 100644
index c28fc6f..0000000
--- a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-rp.https-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-This is a testharness.js-based test.
-PASS Set up the test environment
-PASS Bad rp: rp missing
-PASS Bad rp: rp null
-PASS Bad rp: rp is string
-PASS Bad rp: rp is empty object
-FAIL Bad rp: id is object promise_rejects_js: Expected bad parameters to fail function "function() { throw e }" threw object "SecurityError: The relying party ID is not a registrable domain suffix of, nor equal to the current domain." ("SecurityError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
-PASS Bad rp: id is null
-PASS Bad rp: id is empty String
-PASS Bad rp: id is invalid domain (has space)
-PASS Bad rp: id is invalid domain (starts with dash)
-PASS Bad rp: id is invalid domain (starts with number)
-PASS rp missing name
-FAIL Bad rp: name is object assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-FAIL Bad rp: name is null assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-FAIL Bad rp: name is empty String assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-FAIL Bad rp: icon is object promise_rejects_js: Expected bad parameters to fail function "function() { throw e }" threw object "SecurityError: 'rp.icon' should be a secure URL" ("SecurityError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
-FAIL Bad rp: icon is null promise_rejects_js: Expected bad parameters to fail function "function() { throw e }" threw object "SecurityError: 'rp.icon' should be a secure URL" ("SecurityError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
-FAIL Bad rp: icon is empty String assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-PASS Bad rp: icon is insecure
-PASS Clean up the test environment
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-rp.https.html b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-rp.https.html
index cbd86b8..8886cc1 100644
--- a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-rp.https.html
+++ b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-badargs-rp.https.html
@@ -21,7 +21,6 @@
     new CreateCredentialsTest("options.publicKey.rp", {}).runTest("Bad rp: rp is empty object", TypeError);
 
     // // rp.id
-    new CreateCredentialsTest("options.publicKey.rp.id", {}).runTest("Bad rp: id is object", TypeError);
     new CreateCredentialsTest("options.publicKey.rp.id", null).runTest("Bad rp: id is null", "SecurityError");
     new CreateCredentialsTest("options.publicKey.rp.id", "").runTest("Bad rp: id is empty String", "SecurityError");
     new CreateCredentialsTest("options.publicKey.rp.id", "invalid domain.com").runTest("Bad rp: id is invalid domain (has space)", "SecurityError");
@@ -30,17 +29,10 @@
 
     // // rp.name
     new CreateCredentialsTest({path: "options.publicKey.rp.name", value: undefined}).runTest("rp missing name", TypeError);
-    new CreateCredentialsTest("options.publicKey.rp.name", {}).runTest("Bad rp: name is object", TypeError);
-    new CreateCredentialsTest("options.publicKey.rp.name", null).runTest("Bad rp: name is null", TypeError);
-    new CreateCredentialsTest("options.publicKey.rp.name", "").runTest("Bad rp: name is empty String", TypeError);
 
-    // // rp.icon
-    new CreateCredentialsTest("options.publicKey.rp.icon", {}).runTest("Bad rp: icon is object", TypeError);
-    new CreateCredentialsTest("options.publicKey.rp.icon", null).runTest("Bad rp: icon is null", TypeError);
-    new CreateCredentialsTest("options.publicKey.rp.icon", "").runTest("Bad rp: icon is empty String", TypeError);
+    // rp.icon
     new CreateCredentialsTest("options.publicKey.rp.icon", "http://fidoalliance.co.nz/testimages/catimage.png")
       .runTest("Bad rp: icon is insecure", "SecurityError");
-    // // TODO: unicode tests for icon URL (see also: USVString)
 });
 
 /* JSHINT */
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-excludecredentials.https-expected.txt b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-excludecredentials.https-expected.txt
index b7f4ce9..aefa11e9 100644
--- a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-excludecredentials.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-excludecredentials.https-expected.txt
@@ -1,5 +1,4 @@
 This is a testharness.js-based test.
-PASS Set up the test environment
 PASS Bad excludeCredentials: string
 PASS Bad excludeCredentials: empty object
 PASS excludeCredentials missing
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-extensions.https-expected.txt b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-extensions.https-expected.txt
deleted file mode 100644
index 957f89c..0000000
--- a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-extensions.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-PASS Bad extensions: extensions is string
-FAIL Bad extensions: extensions is null assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-FAIL Bad extensions: extensions is empty Array assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-FAIL Bad extensions: extensions is empty ArrayBuffer assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-FAIL Bad extensions: malformatted JSON assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-FAIL Bad extensions: JavaScript object assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-FAIL Bad extensions: extension ID too long assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
-PASS extensions is a nonsensical JSON string
-PASS empty appid in create request
-PASS null appid in create request
-PASS appid in create request
-PASS Clean up the test environment
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-extensions.https.html b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-extensions.https.html
index 036200dbb..46cab30 100644
--- a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-extensions.https.html
+++ b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-extensions.https.html
@@ -21,15 +21,6 @@
 
     // bad extension values
     new CreateCredentialsTest("options.publicKey.extensions", "hi mom").runTest("Bad extensions: extensions is string", TypeError);
-    new CreateCredentialsTest("options.publicKey.extensions", null).runTest("Bad extensions: extensions is null", TypeError);
-    new CreateCredentialsTest("options.publicKey.extensions", []).runTest("Bad extensions: extensions is empty Array", TypeError);
-    new CreateCredentialsTest("options.publicKey.extensions", new ArrayBuffer(0)).runTest("Bad extensions: extensions is empty ArrayBuffer", TypeError);
-    var badJson = '{"foo": true, "bar: "yup"}'; // missing quote after "bar"
-    new CreateCredentialsTest("options.publicKey.extensions", {foo: badJson}).runTest("Bad extensions: malformatted JSON", TypeError);
-    new CreateCredentialsTest("options.publicKey.extensions", {foo: dummyExtension}).runTest("Bad extensions: JavaScript object", TypeError);
-    var badExtId = {};
-    badExtId[createRandomString(65)] = dummyExtension;
-    new CreateCredentialsTest("options.publicKey.extensions", {badExtId: dummyExtension}).runTest("Bad extensions: extension ID too long", TypeError);
 
     // phony extensions
     // TODO: not sure if this should pass or fail
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-passing.https-expected.txt b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-passing.https-expected.txt
index 4de4f55..ea74f1e 100644
--- a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-passing.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-passing.https-expected.txt
@@ -1,5 +1,4 @@
 This is a testharness.js-based test.
-PASS Set up the test environment
 PASS passing credentials.create() with default arguments
 FAIL passing credentials.create() with rpId (host and port) promise_test: Unhandled rejection with value: object "SecurityError: The relying party ID is not a registrable domain suffix of, nor equal to the current domain."
 PASS passing credentials.create() with rpId (hostname)
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-pubkeycredparams.https-expected.txt b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-pubkeycredparams.https-expected.txt
index f4b9e50..878f2ddf 100644
--- a/third_party/blink/web_tests/external/wpt/webauthn/createcredential-pubkeycredparams.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webauthn/createcredential-pubkeycredparams.https-expected.txt
@@ -1,5 +1,4 @@
 This is a testharness.js-based test.
-PASS Set up the test environment
 PASS Bad pubKeyCredParams: pubKeyCredParams is undefined
 PASS Bad pubKeyCredParams: pubKeyCredParams is string
 PASS Bad pubKeyCredParams: pubKeyCredParams is null
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/getcredential-extensions.https-expected.txt b/third_party/blink/web_tests/external/wpt/webauthn/getcredential-extensions.https-expected.txt
index 7f8bf6c..402ef6fc 100644
--- a/third_party/blink/web_tests/external/wpt/webauthn/getcredential-extensions.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webauthn/getcredential-extensions.https-expected.txt
@@ -1,5 +1,4 @@
 This is a testharness.js-based test.
-PASS Set up the test environment
 PASS Bad extensions: extensions is string
 FAIL Bad extensions: extensions is null assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
 FAIL Bad extensions: extensions is empty Array assert_unreached: Should have rejected: Expected bad parameters to fail Reached unreachable code
diff --git a/third_party/blink/web_tests/external/wpt/webauthn/getcredential-passing.https-expected.txt b/third_party/blink/web_tests/external/wpt/webauthn/getcredential-passing.https-expected.txt
index ddbee33..cbb45aa 100644
--- a/third_party/blink/web_tests/external/wpt/webauthn/getcredential-passing.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webauthn/getcredential-passing.https-expected.txt
@@ -1,5 +1,4 @@
 This is a testharness.js-based test.
-PASS Set up the test environment
 PASS passing credentials.get() with default args
 PASS passing credentials.create() with no timeout
 PASS rpId undefined
diff --git a/third_party/blink/web_tests/inspector-protocol/emulation/set-vision-deficiency-expected.txt b/third_party/blink/web_tests/inspector-protocol/emulation/set-vision-deficiency-expected.txt
deleted file mode 100644
index 1699891..0000000
--- a/third_party/blink/web_tests/inspector-protocol/emulation/set-vision-deficiency-expected.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-Tests that vision deficiencies can be emulated.
-<p>Emulating none: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAFRJREFUWIXt18ENACAMw8DA5N0chnCQePgGiKz+ujI5aZru3K6uPWAgZSBlIGUgZSBlIGUgZSBlILWS030iyr6/oIGUgZSBlIGUgZSBlIGUgZSB1AXNwwVLtuf+dAAAAABJRU5ErkJggg==">
-<p>Emulating achromatomaly: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAGFJREFUWIXt10ENwCAUBFFAATbQxBErtVNdyAARs016mCdgM/m3X8capwTtdybnSouufcBAykDKQMpAykDKQMpAykDKQKr2/kR/krTfX9BAykDKQMpAykDKQMpAykDKQOoCayUFlTeCp/sAAAAASUVORK5CYII=">
-<p>Emulating achromatopsia: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAFpJREFUWIXt10ENADEMxMD2eARR2IfQFYS3Uh8eACsrv+zu/lfQzCTn1hddu8BAykDKQMpAykDKQMpAykDKQGpXVfQnSXv+ggZSBlIGUgZSBlIGUgZSBlIGUgcPVwX7L3bHewAAAABJRU5ErkJggg==">
-<p>Emulating blurredVision: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAehJREFUWIXtl81u4jAURo8TN7RpASXqohKLvsG89DzXLJFm1ZZCyb89i5tAWtAQYlfqIp90FWEl1yfX186H4tdvy3wN8Qvc7CGoQVnA8n8psAqMhiqGfQrbFWye4X0FWSrjRst9qAv5zkuzWEPyBx7+QrQDXbZwAwBRUEdQPsDuSYbKOeQJFAtQZhTUZ8C7F4FbrGG2EUBlhgHaQACLhQzlCURbCAtZicOLjqueAN7spXK3G5i9jQCcyfz5TlokLI9tMp6rBxjUkjQsBU4X1wEC1GUPrOn18KUcQwCV4WwM6kF6z1hvVfsMeJA9E5d07hk/lesUeMv0TZoAXTUBumoCdFXvHFRf4pKuvX+cNDbgJIDBn7qT8AusMRqaSKKO2rmvdDPd80aDDb1CaqpY/Fy+FKZRdmspOaq4B9pV0xUwS8VsAkRLN8OapWJYmxmYEB/9qXlfCUue+LH8WQplfKyiozSbZ3nr7bb1dM0AuB6kCWVZy7nAfTxCdS9jHjaNVDBLxKh2Nl0NBOwmN1qcdRVDed/7s+ReQcXtqyWor1ja0xSHpe5OBKPbHnTfKIqwsIddqxgJ2F39n4cK1bREri5YHa+2/9s5q/UM6FdfvsU/Tz/ezUyArpoAXTUBumoCdNUE6KoJ0FX/AAow6PKlz4YvAAAAAElFTkSuQmCC">
-<p>Emulating none: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAFRJREFUWIXt18ENACAMw8DA5N0chnCQePgGiKz+ujI5aZru3K6uPWAgZSBlIGUgZSBlIGUgZSBlILWS030iyr6/oIGUgZSBlIGUgZSBlIGUgZSB1AXNwwVLtuf+dAAAAABJRU5ErkJggg==">
-<p>Emulating deuteranomaly: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAGFJREFUWIXt1zENwDAQBMG3IaQ0uWAzHGNwHxoOiL1IKXYAnFbffRv3dSromSs5Vz269gEDKQMpAykDKQMpAykDKQMpA6lWtaM/SdrvL2ggZSBlIGUgZSBlIGUgZSBlIPUCZwcGA/y3590AAAAASUVORK5CYII=">
-<p>Emulating deuteranopia: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAF9JREFUWIXt17ENwCAQBMGHYohdqKuhEsduBorYQyLYKeC0+uzbM8aqoO9/k3PVo2sHGEgZSBlIGUgZSBlIGUgZSBlItaoZ/UnSrr+ggZSBlIGUgZSBlIGUgZSBlIHUBmSnBbHzufJtAAAAAElFTkSuQmCC">
-<p>Emulating none: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAFRJREFUWIXt18ENACAMw8DA5N0chnCQePgGiKz+ujI5aZru3K6uPWAgZSBlIGUgZSBlIGUgZSBlILWS030iyr6/oIGUgZSBlIGUgZSBlIGUgZSB1AXNwwVLtuf+dAAAAABJRU5ErkJggg==">
-<p>Emulating protanomaly: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAGFJREFUWIXt1zENwDAQBMG3EaQzsqA0GENwHRQOiL1IKXYAnFbffRv3dSromSs5Vz269gEDKQMpAykDKQMpAykDKQMpA6lWtaM/SdrvL2ggZSBlIGUgZSBlIGUgZSBlIPUC9+8GC+XyxMUAAAAASUVORK5CYII=">
-<p>Emulating protanopia: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAF9JREFUWIXt10ENwCAQBdEFI5VBahcnnEhVURHzm/QwT8DPZG/bxn2dCnr2TM5Vj659wEDKQMpAykDKQMpAykDKQMpAqlWt6E+S9vsLGkgZSBlIGUgZSBlIGUgZSBlIvZuvBc8DKP+1AAAAAElFTkSuQmCC">
-<p>Emulating tritanomaly: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAFxJREFUWIXt18EJACEQBEEVczA78zQhoxA0iJ6De3QFMDT729rnuCXo7JWcKy269gEDKQMpAykDKQMpAykDKQMpA6me/iHSfn9BAykDKQMpAykDKQMpAykDKQOpB4zjBwAkamC5AAAAAElFTkSuQmCC">
-<p>Emulating tritanopia: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAF5JREFUWIXt17ENwCAQBEGwEC0RuQBaoP8qnJgi9pAIdgo4rT772sf7l6BvzeRceaJrBxhIGUgZSBlIGUgZSBlIGUgZSLX0D5F2/QUNpAykDKQMpAykDKQMpAykDKQ2h2YFBXR3unwAAAAASUVORK5CYII=">
-<p>Emulating tritanopia: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAF5JREFUWIXt17ENwCAQBEGwEC0RuQBaoP8qnJgi9pAIdgo4rT772sf7l6BvzeRceaJrBxhIGUgZSBlIGUgZSBlIGUgZSLX0D5F2/QUNpAykDKQMpAykDKQMpAykDKQ2h2YFBXR3unwAAAAASUVORK5CYII=">
-<p>Emulating some-invalid-deficiency: 
-{
-  "code": -32602,
-  "message": "Unknown vision deficiency type"
-}
-<p>Emulating : 
-{
-  "code": -32602,
-  "message": "Unknown vision deficiency type"
-}
-<p>Navigating&mldr;
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAF5JREFUWIXt17ENwCAQBEGwEC0RuQBaoP8qnJgi9pAIdgo4rT772sf7l6BvzeRceaJrBxhIGUgZSBlIGUgZSBlIGUgZSLX0D5F2/QUNpAykDKQMpAykDKQMpAykDKQ2h2YFBXR3unwAAAAASUVORK5CYII=">
-<p>Emulating achromatopsia: 
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAFpJREFUWIXt10ENADEMxMD2eARR2IfQFYS3Uh8eACsrv+zu/lfQzCTn1hddu8BAykDKQMpAykDKQMpAykDKQGpXVfQnSXv+ggZSBlIGUgZSBlIGUgZSBlIGUgcPVwX7L3bHewAAAABJRU5ErkJggg==">
-
diff --git a/third_party/blink/web_tests/inspector-protocol/emulation/set-vision-deficiency.js b/third_party/blink/web_tests/inspector-protocol/emulation/set-vision-deficiency.js
deleted file mode 100644
index 6adc8ca2..0000000
--- a/third_party/blink/web_tests/inspector-protocol/emulation/set-vision-deficiency.js
+++ /dev/null
@@ -1,59 +0,0 @@
-(async function(testRunner) {
-  const {page, session, dp} = await testRunner.startBlank(
-      'Tests that vision deficiencies can be emulated.');
-  // Note: the output log for this test can be viewed as HTML to
-  // simplify review.
-
-  await session.navigate('../resources/vision-deficiency.html');
-
-  async function logScreenshotData() {
-    const response = await dp.Page.captureScreenshot({
-      clip: {
-        x: 0,
-        y: 0,
-        width: 40,
-        height: 40,
-        scale: 1,
-      },
-    });
-    const imageData = response.result.data;
-    testRunner.log(`<img src="data:image/png;base64,${imageData}">`);
-  }
-
-  async function setEmulatedVisionDeficiency(id) {
-    testRunner.log(`<p>Emulating ${id}: `);
-    const response = await dp.Emulation.setEmulatedVisionDeficiency({
-      type: id,
-    });
-    if (response.error) {
-      testRunner.log(JSON.stringify(response.error, null, 2));
-      return;
-    }
-    await logScreenshotData();
-  }
-
-  await setEmulatedVisionDeficiency('none');
-  await setEmulatedVisionDeficiency('achromatomaly');
-  await setEmulatedVisionDeficiency('achromatopsia');
-  await setEmulatedVisionDeficiency('blurredVision');
-  await setEmulatedVisionDeficiency('none');
-  await setEmulatedVisionDeficiency('deuteranomaly');
-  await setEmulatedVisionDeficiency('deuteranopia');
-  await setEmulatedVisionDeficiency('none');
-  await setEmulatedVisionDeficiency('protanomaly');
-  await setEmulatedVisionDeficiency('protanopia');
-  await setEmulatedVisionDeficiency('tritanomaly');
-  await setEmulatedVisionDeficiency('tritanopia');
-  // Test setting the already-active vision deficiency.
-  await setEmulatedVisionDeficiency('tritanopia');
-  // Test setting unknown vision deficiencies.
-  await setEmulatedVisionDeficiency('some-invalid-deficiency');
-  await setEmulatedVisionDeficiency('');
-
-  testRunner.log(`<p>Navigating&mldr;`);
-  await session.navigate('../resources/vision-deficiency.html');
-  await logScreenshotData();
-  await setEmulatedVisionDeficiency('achromatopsia');
-
-  testRunner.completeTest();
-});
diff --git a/third_party/blink/web_tests/inspector-protocol/resources/vision-deficiency.css b/third_party/blink/web_tests/inspector-protocol/resources/vision-deficiency.css
deleted file mode 100644
index 17733910..0000000
--- a/third_party/blink/web_tests/inspector-protocol/resources/vision-deficiency.css
+++ /dev/null
@@ -1,12 +0,0 @@
-html {
-  background: blue;
-}
-html, body {
-  margin: 0;
-  padding: 0;
-}
-div {
-  width: 20px;
-  height: 20px;
-  background: green;
-}
diff --git a/third_party/blink/web_tests/inspector-protocol/resources/vision-deficiency.html b/third_party/blink/web_tests/inspector-protocol/resources/vision-deficiency.html
deleted file mode 100644
index 3273a67..0000000
--- a/third_party/blink/web_tests/inspector-protocol/resources/vision-deficiency.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<!DOCTYPE html>
-<!--
-Note: These tests should pass even when the document's CSP disallows
-      data: URLs.
--->
-<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
-<link rel="stylesheet" href="vision-deficiency.css">
-<div></div>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f53f57b0..71c81e1 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -55804,6 +55804,8 @@
   <int value="20" label="Cleanup"/>
   <int value="21" label="WebSchedulingUserInteraction"/>
   <int value="22" label="WebSchedulingBestEffort"/>
+  <int value="24" label="WebScheduling"/>
+  <int value="25" label="NonWaking"/>
 </enum>
 
 <enum name="RendererSchedulerTaskType">
@@ -55867,6 +55869,7 @@
   <int value="57" label="ApplicationLifeCycle"/>
   <int value="58" label="BackgroundFetch"/>
   <int value="59" label="Permission"/>
+  <int value="60" label="ServiceWorkerClientMessage"/>
   <int value="61" label="InternalContentCapture"/>
   <int value="62" label="MainThreadTaskQueueMemoryPurge"/>
   <int value="63" label="InternalNavigation"/>
@@ -55874,6 +55877,8 @@
   <int value="65" label="InternalContinueScriptLoading"/>
   <int value="66" label="kWebLocks"/>
   <int value="67" label="ExperimentalWebScheduling"/>
+  <int value="68" label="InternalFrameLifecycleControl"/>
+  <int value="69" label="MainThreadTaskQueueNonWaking"/>
 </enum>
 
 <enum name="RendererSchedulerTaskUseCase">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 9eeefde4..90efef65 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -16195,7 +16195,7 @@
   <summary>Records whenever a Blimp tab toggles visibility.</summary>
 </histogram>
 
-<histogram base="true" name="Blink.Animate.UpdateTime" units="microseconds"
+<histogram name="Blink.Animate.UpdateTime" units="microseconds"
     expires_after="2020-07-06">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -17042,7 +17042,7 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.Compositing.UpdateTime" units="microseconds"
+<histogram name="Blink.Compositing.UpdateTime" units="microseconds"
     expires_after="2021-03-01">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" -->
 
@@ -17059,8 +17059,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.CompositingCommit.UpdateTime"
-    units="microseconds" expires_after="2021-03-01">
+<histogram name="Blink.CompositingCommit.UpdateTime" units="microseconds"
+    expires_after="2021-03-01">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" -->
 
   <owner>schenney@chromium.org</owner>
@@ -17518,8 +17518,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.ForcedStyleAndLayout.UpdateTime"
-    units="microseconds" expires_after="2020-07-06">
+<histogram name="Blink.ForcedStyleAndLayout.UpdateTime" units="microseconds"
+    expires_after="2020-07-06">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" -->
 
   <owner>schenney@chromium.org</owner>
@@ -17547,8 +17547,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.HandleInputEvents.UpdateTime"
-    units="microseconds" expires_after="2020-07-26">
+<histogram name="Blink.HandleInputEvents.UpdateTime" units="microseconds"
+    expires_after="2020-07-26">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" -->
 
   <owner>schenney@chromium.org</owner>
@@ -17561,8 +17561,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.HitTestDocumentUpdate.UpdateTime"
-    units="microseconds" expires_after="2020-07-26">
+<histogram name="Blink.HitTestDocumentUpdate.UpdateTime" units="microseconds"
+    expires_after="2020-07-26">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" -->
 
   <owner>schenney@chromium.org</owner>
@@ -17649,8 +17649,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.ImplCompositorCommit.UpdateTime"
-    units="microseconds" expires_after="2021-03-01">
+<histogram name="Blink.ImplCompositorCommit.UpdateTime" units="microseconds"
+    expires_after="2021-03-01">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" -->
 
   <owner>schenney@chromium.org</owner>
@@ -17665,8 +17665,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.IntersectionObservation.UpdateTime"
-    units="microseconds" expires_after="2020-07-06">
+<histogram name="Blink.IntersectionObservation.UpdateTime" units="microseconds"
+    expires_after="2020-07-06">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" -->
 
   <owner>schenney@chromium.org</owner>
@@ -17689,7 +17689,7 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.Layout.UpdateTime" units="microseconds"
+<histogram name="Blink.Layout.UpdateTime" units="microseconds"
     expires_after="2020-08-24">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" -->
 
@@ -17957,7 +17957,7 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.MainFrame.WaitForCommitRatio" units="%"
+<histogram name="Blink.MainFrame.WaitForCommitRatio" units="%"
     expires_after="2020-07-06">
   <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
@@ -18159,7 +18159,7 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.Paint.UpdateTime" units="microseconds"
+<histogram name="Blink.Paint.UpdateTime" units="microseconds"
     expires_after="2020-08-10">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -18177,8 +18177,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.PaintInvalidation.UpdateTime"
-    units="microseconds" expires_after="2018-02-22">
+<histogram name="Blink.PaintInvalidation.UpdateTime" units="microseconds"
+    expires_after="2018-02-22">
   <obsolete>
     SlimmingPaintInvalidation is enabled by default, so this histogram is no
     longer being logged. Was removed in 02-2018.
@@ -18190,7 +18190,7 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.PrePaint.UpdateTime" units="microseconds"
+<histogram name="Blink.PrePaint.UpdateTime" units="microseconds"
     expires_after="2020-08-17">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -18210,7 +18210,7 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.ProxyCommit.UpdateTime" units="microseconds"
+<histogram name="Blink.ProxyCommit.UpdateTime" units="microseconds"
     expires_after="2020-07-06">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -18520,8 +18520,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.ScrollingCoordinator.UpdateTime"
-    units="microseconds" expires_after="2020-07-06">
+<histogram name="Blink.ScrollingCoordinator.UpdateTime" units="microseconds"
+    expires_after="2020-07-06">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -18706,7 +18706,7 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.Style.UpdateTime" units="microseconds"
+<histogram name="Blink.Style.UpdateTime" units="microseconds"
     expires_after="2020-07-06">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
@@ -18724,8 +18724,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.StyleAndLayout.UpdateTime"
-    units="microseconds" expires_after="2020-08-10">
+<histogram name="Blink.StyleAndLayout.UpdateTime" units="microseconds"
+    expires_after="2020-08-10">
   <obsolete>
     Replaced with separate Style and Layout metrics and removed in M81.
   </obsolete>
@@ -19262,8 +19262,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Blink.WaitForCommit.UpdateTime"
-    units="microseconds" expires_after="2021-03-01">
+<histogram name="Blink.WaitForCommit.UpdateTime" units="microseconds"
+    expires_after="2021-03-01">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimeAggregatedSuffixes" -->
 
   <owner>schenney@chromium.org</owner>
@@ -23756,7 +23756,7 @@
 </histogram>
 
 <histogram name="ChromeOS.SAML.APILogin" enum="ChromeOSSamlApiUsed"
-    expires_after="2020-04-02">
+    expires_after="2020-10-02">
   <owner>mslus@chromium.org</owner>
   <owner>emaxx@chromium.org</owner>
   <summary>
@@ -79691,7 +79691,7 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.DirectoryType"
-    enum="DownloadLocationDirectoryType" expires_after="2020-03-01">
+    enum="DownloadLocationDirectoryType" expires_after="2021-03-01">
   <owner>xingliu@chromium.org</owner>
   <owner>dtrainor@chromium.org</owner>
   <owner>qinmin@chromium.org</owner>
@@ -110501,6 +110501,10 @@
 <histogram
     name="PageLoad.Experimental.PaintTiming.NavigationToLargestImagePaint"
     units="ms" expires_after="2020-04-23">
+  <obsolete>
+    Removed Mar 2020 in favor of
+    PageLoad.PaintTiming.NavigationToLargestContentfulPaint.
+  </obsolete>
   <owner>maxlg@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -110514,6 +110518,10 @@
 <histogram
     name="PageLoad.Experimental.PaintTiming.NavigationToLargestTextPaint"
     units="ms" expires_after="2020-07-26">
+  <obsolete>
+    Removed Mar 2020 in favor of
+    PageLoad.PaintTiming.NavigationToLargestContentfulPaint.
+  </obsolete>
   <owner>maxlg@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -185051,6 +185059,8 @@
   <suffix name="GenerateRequest" label="GenerateRequest promises only."/>
   <suffix name="LoadSession" label="LoadSession promises only."/>
   <suffix name="RemoveSession" label="RemoveSession promises only."/>
+  <suffix name="SetServerCertificate"
+      label="SetServerCertificate promises only."/>
   <suffix name="UpdateSession" label="UpdateSession promises only."/>
   <affected-histogram name="Media.EME.ClearKey"/>
   <affected-histogram name="Media.EME.Unknown"/>
diff --git a/ui/android/java/src/org/chromium/ui/interpolators/BakedBezierInterpolator.java b/ui/android/java/src/org/chromium/ui/interpolators/BakedBezierInterpolator.java
index e461f6c..9940872 100644
--- a/ui/android/java/src/org/chromium/ui/interpolators/BakedBezierInterpolator.java
+++ b/ui/android/java/src/org/chromium/ui/interpolators/BakedBezierInterpolator.java
@@ -4,11 +4,12 @@
 
 package org.chromium.ui.interpolators;
 
-import android.support.v4.view.animation.FastOutLinearInInterpolator;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
 import android.view.animation.Interpolator;
 
+import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
+import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
+import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
+
 /**
  * A pre-baked bezier-curved interpolator for quantum-paper transitions.
  * TODO(dtrainor): Move to the API Compatibility version iff that supports the curves we need and
diff --git a/ui/color/BUILD.gn b/ui/color/BUILD.gn
index 8413ed17..9c4976de 100644
--- a/ui/color/BUILD.gn
+++ b/ui/color/BUILD.gn
@@ -17,96 +17,83 @@
   sources = [
     "color_id.h",
     "color_id_macros.inc",
+    "color_mixer.h",
     "color_provider.h",
+    "color_set.h",
   ]
 
-  public_deps = [ ":color_buildflags" ]
-
-  if (use_color_pipeline) {
-    sources += [
-      "color_mixer.h",
-      "color_set.h",
-    ]
-
-    public_deps += [
-      "//base:base",
-      "//skia:skia",
-    ]
-  } else {
-    public_deps += [
-      "//ui/base:base",
-      "//ui/native_theme:native_theme",
-    ]
-  }
+  public_deps = [
+    ":color_buildflags",
+    "//base:base",
+    "//skia:skia",
+  ]
 }
 
-if (use_color_pipeline) {
-  jumbo_component("color") {
-    sources = [
-      "color_mixer.cc",
-      "color_provider.cc",
-      "color_recipe.cc",
-      "color_recipe.h",
-      "color_set.cc",
-      "color_transform.cc",
-      "color_transform.h",
-    ]
+jumbo_component("color") {
+  sources = [
+    "color_mixer.cc",
+    "color_provider.cc",
+    "color_recipe.cc",
+    "color_recipe.h",
+    "color_set.cc",
+    "color_transform.cc",
+    "color_transform.h",
+  ]
 
-    defines = [ "IS_COLOR_IMPL" ]
+  defines = [ "IS_COLOR_IMPL" ]
 
-    public_deps = [
-      ":color_headers",
-      "//base:base",
-      "//skia:skia",
-      "//ui/gfx:color_utils",
-    ]
-  }
+  public_deps = [
+    ":color_headers",
+    "//base:base",
+    "//skia:skia",
+    "//ui/gfx:color_utils",
+  ]
+}
 
-  test("color_unittests") {
-    testonly = true
+test("color_unittests") {
+  testonly = true
 
-    sources = [
-      "color_mixer_unittest.cc",
-      "color_provider_unittest.cc",
-      "color_recipe_unittest.cc",
-      "color_test_ids.h",
-      "color_transform_unittest.cc",
-      "run_all_unittests.cc",
-    ]
+  sources = [
+    "color_mixer_unittest.cc",
+    "color_provider_unittest.cc",
+    "color_recipe_unittest.cc",
+    "color_test_ids.h",
+    "color_transform_unittest.cc",
+    "run_all_unittests.cc",
+  ]
 
-    deps = [
-      ":color",
-      "//base/test:test_support",
-      "//testing/gtest",
-    ]
-  }
+  deps = [
+    ":color",
+    "//base/test:test_support",
+    "//testing/gtest",
+  ]
+}
 
-  jumbo_component("mixers") {
-    sources = [
-      "color_mixers.h",
-      "core_default_color_mixer.cc",
-      "ui_color_mixer.cc",
-    ]
+jumbo_component("mixers") {
+  sources = [
+    "color_mixers.h",
+    "core_default_color_mixer.cc",
+    "ui_color_mixer.cc",
+  ]
 
-    defines = [ "IS_COLOR_IMPL" ]
+  defines = [ "IS_COLOR_IMPL" ]
 
-    deps = [
-      ":color",
-      ":color_headers",
-      "//skia",
-      "//ui/gfx:color_utils",
-    ]
+  deps = [
+    ":color",
+    ":color_headers",
+    "//skia",
+    "//ui/gfx:color_utils",
+  ]
 
-    public_deps = [ "//base" ]
+  public_deps = [ "//base" ]
 
-    if (is_chromeos) {
-      sources += [ "cros/native_color_mixer.cc" ]
-    } else if (is_linux) {
-      sources += [ "linux/native_color_mixer.cc" ]
-    } else if (is_mac) {
-      sources += [ "mac/native_color_mixer.cc" ]
-    } else if (is_win) {
-      sources += [ "win/native_color_mixer.cc" ]
-    }
+  if (is_chromeos) {
+    sources += [ "cros/native_color_mixer.cc" ]
+  } else if (is_linux) {
+    sources += [ "linux/native_color_mixer.cc" ]
+  } else if (is_mac) {
+    sources += [ "mac/native_color_mixer.cc" ]
+  } else if (is_win) {
+    sources += [ "win/native_color_mixer.cc" ]
   }
 }
diff --git a/ui/color/DEPS b/ui/color/DEPS
index c5a8c5f9..41134f5 100644
--- a/ui/color/DEPS
+++ b/ui/color/DEPS
@@ -1,6 +1,4 @@
 include_rules = [
   "+third_party/skia/include",
-  "+ui/base/theme_provider.h",  # used by color_provider.h when !USE_COLOR_PIPELINE.
   "+ui/gfx",
-  "+ui/native_theme/native_theme.h",  # used by color_id.h when !USE_COLOR_PIPELINE.
 ]
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index e5b2d49d..ccf3d6c 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -9,14 +9,6 @@
 #include "build/buildflag.h"
 #include "ui/color/color_buildflags.h"
 
-#if !BUILDFLAG(USE_COLOR_PIPELINE)
-#include "ui/native_theme/native_theme.h"  // nogncheck
-
-#if defined(OS_WIN)
-#include <windows.h>
-#endif
-#endif
-
 // clang-format off
 #define CROSS_PLATFORM_COLOR_IDS \
   /* Core color concepts */ \
@@ -206,8 +198,6 @@
 
 #include "ui/color/color_id_macros.inc"
 
-#if BUILDFLAG(USE_COLOR_PIPELINE)
-
 // ColorSetId contains identifiers for all distinct color sets known to the core
 // UI layer.  As with ColorId, embedders can extend this enum with additional
 // values that are understood by the ColorProvider implementation.  Embedders
@@ -237,8 +227,6 @@
 // Verifies that |id| is a color set ID, not a color ID.
 #define DCHECK_COLOR_SET_ID_VALID(id) DCHECK_GE(id, kUiColorSetsStart)
 
-#endif  // BUILDFLAG(USE_COLOR_PIPELINE)
-
 }  // namespace ui
 
 #endif  // UI_COLOR_COLOR_ID_H_
diff --git a/ui/color/color_id_macros.inc b/ui/color/color_id_macros.inc
index 66e4a492..112e49b 100644
--- a/ui/color/color_id_macros.inc
+++ b/ui/color/color_id_macros.inc
@@ -20,21 +20,11 @@
 #define D2(enum_name, enum_value) enum_name = enum_value
 #endif  // defined(STRINGIZE_COLOR_IDS)
 // Select which token in the declaration is the assigned value.
-#if BUILDFLAG(USE_COLOR_PIPELINE)
 // Use first and optional third token, ignore optional second.
 #define E1(enum_name) D1(enum_name)
 #define E2(enum_name, old_enum_name) D1(enum_name)
 #define E3(enum_name, old_enum_name, enum_value) D2(enum_name, enum_value)
 #define E_CPONLY(...) E(__VA_ARGS__)
-#else
-// Use first and mandatory second token, ignore optional third.
-#define E1(enum_name) \
-  static_assert(false, "New-style color compiled for !USE_COLOR_PIPELINE")
-#define E2(enum_name, old_enum_name) D2(enum_name, old_enum_name)
-#define E3(enum_name, old_enum_name, enum_value) D2(enum_name, old_enum_name)
-// Ignore any new color id defined only for color pipeline enabled.
-#define E_CPONLY(...)
-#endif  // BUILDFLAG(USE_COLOR_PIPELINE)
 #define GET_E(_1, _2, _3, macro_name, ...) macro_name
 #define E(...) GET_E(__VA_ARGS__, E3, E2, E1)(__VA_ARGS__),
 #else
diff --git a/ui/color/color_provider.h b/ui/color/color_provider.h
index 1c60ea9..0a7f428fc 100644
--- a/ui/color/color_provider.h
+++ b/ui/color/color_provider.h
@@ -8,21 +8,14 @@
 #include <forward_list>
 #include <map>
 
-#include "ui/color/color_buildflags.h"
-
-#if BUILDFLAG(USE_COLOR_PIPELINE)
 #include "base/component_export.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/color/color_buildflags.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_mixer.h"
-#else
-#include "ui/base/theme_provider.h"  // nogncheck
-#endif
 
 namespace ui {
 
-#if BUILDFLAG(USE_COLOR_PIPELINE)
-
 // A ColorProvider holds the complete pipeline of ColorMixers that compute
 // result colors for UI elements.  ColorProvider is meant to be a long-lived
 // object whose internal list of mixers does not change after initial
@@ -57,12 +50,6 @@
   mutable std::map<ColorId, SkColor> cache_;
 };
 
-#else
-
-using ColorProvider = ThemeProvider;
-
-#endif  // !BUILDFLAG(USE_COLOR_PIPELINE)
-
 }  // namespace ui
 
 #endif  // UI_COLOR_COLOR_PROVIDER_H_
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index 901ac87..baa857e 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -479,6 +479,19 @@
     case NativeTheme::kColorId_DefaultIconColor:
       return gfx::kGoogleGrey700;
 
+    // Sync info container
+    case NativeTheme::kColorId_SyncInfoContainerPaused:
+      return SkColorSetA(base_theme->GetSystemColor(
+                             NativeTheme::kColorId_ProminentButtonColor),
+                         16);
+    case NativeTheme::kColorId_SyncInfoContainerError:
+      return SkColorSetA(
+          base_theme->GetSystemColor(NativeTheme::kColorId_AlertSeverityHigh),
+          16);
+    case NativeTheme::kColorId_SyncInfoContainerNoPrimaryAccount:
+      return base_theme->GetSystemColor(
+          NativeTheme::kColorId_BubbleFooterBackground);
+
     case NativeTheme::kColorId_NumColors:
       break;
   }
diff --git a/ui/native_theme/native_theme_color_id.h b/ui/native_theme/native_theme_color_id.h
index 72f1ea6..85a7da0 100644
--- a/ui/native_theme/native_theme_color_id.h
+++ b/ui/native_theme/native_theme_color_id.h
@@ -75,6 +75,10 @@
   OP(kColorId_SliderTroughMinimal),                                            \
   /* Separator */                                                              \
   OP(kColorId_SeparatorColor),                                                 \
+  /* Sync info container */                                                    \
+  OP(kColorId_SyncInfoContainerPaused),                                        \
+  OP(kColorId_SyncInfoContainerError),                                         \
+  OP(kColorId_SyncInfoContainerNoPrimaryAccount),                              \
   /* TabbedPane */                                                             \
   OP(kColorId_TabTitleColorActive),                                            \
   OP(kColorId_TabTitleColorInactive),                                          \
@@ -122,6 +126,7 @@
   OP(kColorId_AlertSeverityHigh),                                              \
   /* Colors for icons in secondary UI (content settings, help button, etc). */ \
   OP(kColorId_DefaultIconColor)
+
 // clang-format on
 
 #endif  // UI_NATIVE_THEME_NATIVE_THEME_COLOR_ID_H_
diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc
index 3c74784..9799a58 100644
--- a/ui/views/widget/root_view.cc
+++ b/ui/views/widget/root_view.cc
@@ -427,13 +427,15 @@
 
   // Reset mouse_pressed_handler_ to indicate that no processing is occurring.
   mouse_pressed_handler_ = nullptr;
+
+  const bool last_click_was_handled = (last_click_handler_ != nullptr);
   last_click_handler_ = nullptr;
 
   // In the event that a double-click is not handled after traversing the
   // entire hierarchy (even as a single-click when sent to a different view),
   // it must be marked as handled to avoid anything happening from default
   // processing if it the first click-part was handled by us.
-  return event.flags() & ui::EF_IS_DOUBLE_CLICK;
+  return last_click_was_handled && (event.flags() & ui::EF_IS_DOUBLE_CLICK);
 }
 
 bool RootView::OnMouseDragged(const ui::MouseEvent& event) {
diff --git a/ui/views/widget/root_view_unittest.cc b/ui/views/widget/root_view_unittest.cc
index 48e2372..2a0b24db 100644
--- a/ui/views/widget/root_view_unittest.cc
+++ b/ui/views/widget/root_view_unittest.cc
@@ -860,5 +860,57 @@
   EXPECT_EQ(1, v3->GetEventCount(ui::ET_MOUSE_PRESSED));
 }
 
+// If RootView::OnMousePressed() receives a double-click event that isn't
+// handled by any views, it should still report it as handled if the first click
+// was handled. However, it should *not* if the first click was unhandled.
+// Regression test for https://crbug.com/1055674.
+TEST_F(RootViewTest, DoubleClickHandledIffFirstClickHandled) {
+  Widget widget;
+  Widget::InitParams init_params =
+      CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  init_params.bounds = {100, 100, 100, 100};
+  widget.Init(std::move(init_params));
+  widget.Show();
+  internal::RootView* root_view =
+      static_cast<internal::RootView*>(widget.GetRootView());
+  root_view->SetContentsView(new View());
+
+  View* const contents_view = root_view->GetContentsView();
+  EventCountView* const v1 =
+      contents_view->AddChildView(std::make_unique<EventCountView>());
+
+  contents_view->SetBoundsRect(gfx::Rect(0, 0, 10, 10));
+  v1->SetBoundsRect(gfx::Rect(0, 0, 10, 10));
+
+  ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, gfx::Point(5, 5),
+                               gfx::Point(5, 5), ui::EventTimeForNow(), 0, 0);
+  ui::MouseEvent released_event(ui::ET_MOUSE_RELEASED, gfx::Point(5, 5),
+                                gfx::Point(5, 5), ui::EventTimeForNow(), 0, 0);
+
+  // First click handled, second click unhandled.
+  v1->set_handle_mode(EventCountView::CONSUME_EVENTS);
+  pressed_event.SetClickCount(1);
+  released_event.SetClickCount(1);
+  EXPECT_TRUE(root_view->OnMousePressed(pressed_event));
+  root_view->OnMouseReleased(released_event);
+  v1->set_handle_mode(EventCountView::PROPAGATE_EVENTS);
+  pressed_event.SetClickCount(2);
+  released_event.SetClickCount(2);
+  EXPECT_TRUE(root_view->OnMousePressed(pressed_event));
+  root_view->OnMouseReleased(released_event);
+
+  // Both clicks unhandled.
+  v1->set_handle_mode(EventCountView::PROPAGATE_EVENTS);
+  pressed_event.SetClickCount(1);
+  released_event.SetClickCount(1);
+  EXPECT_FALSE(root_view->OnMousePressed(pressed_event));
+  root_view->OnMouseReleased(released_event);
+  pressed_event.SetClickCount(2);
+  released_event.SetClickCount(2);
+  EXPECT_FALSE(root_view->OnMousePressed(pressed_event));
+  root_view->OnMouseReleased(released_event);
+}
+
 }  // namespace test
 }  // namespace views
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index c5cd9d597..a9869561 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -616,6 +616,9 @@
   for (WidgetObserver& observer : observers_)
     observer.OnWidgetClosing(this);
 
+  if (widget_delegate_)
+    widget_delegate_->WindowWillClose();
+
   native_widget_->Close();
 }
 
diff --git a/ui/views/widget/widget_delegate.h b/ui/views/widget/widget_delegate.h
index fd8ea2e..a3ec229 100644
--- a/ui/views/widget/widget_delegate.h
+++ b/ui/views/widget/widget_delegate.h
@@ -132,13 +132,22 @@
   // Default is true.
   virtual bool ShouldRestoreWindowSize() const;
 
-  // Called when the window closes. The delegate MUST NOT delete itself during
-  // this call, since it can be called afterwards. See DeleteDelegate().
+  // Hooks for the end of the Widget/Window lifecycle. As of this writing, these
+  // callbacks happen like so:
+  //   1. Client code calls Widget::CloseWithReason()
+  //   2. WidgetDelegate::WindowWillClose() is called
+  //   3. NativeWidget teardown (maybe async) starts OR the operating system
+  //      abruptly closes the backing native window
+  //   4. WidgetDelegate::WindowClosing() is called
+  //   5. NativeWidget teardown completes, Widget teardown starts
+  //   6. WidgetDelegate::DeleteDelegate() is called
+  //   7. Widget teardown finishes, Widget is deleted
+  // At step 3, the "maybe async" is controlled by whether the close is done via
+  // Close() or CloseNow().
+  // Important note: for OS-initiated window closes, steps 1 and 2 don't happen
+  // - i.e, WindowWillClose() is never invoked.
+  virtual void WindowWillClose() {}
   virtual void WindowClosing() {}
-
-  // Called when the window is destroyed. No events must be sent or received
-  // after this point. The delegate can use this opportunity to delete itself at
-  // this time if necessary.
   virtual void DeleteDelegate() {}
 
   // Called when the user begins/ends to change the bounds of the window.
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc
index 58ce3ab..0f34750 100644
--- a/ui/views/window/dialog_client_view.cc
+++ b/ui/views/window/dialog_client_view.cc
@@ -86,41 +86,12 @@
     dialog->RemoveObserver(this);
 }
 
-void DialogClientView::AcceptWindow() {
-  // Only notify the delegate once. See |delegate_allowed_close_|'s comment.
-  if (!delegate_allowed_close_ && GetDialogDelegate()->Accept()) {
-    delegate_allowed_close_ = true;
-    GetWidget()->CloseWithReason(
-        views::Widget::ClosedReason::kAcceptButtonClicked);
-  }
-}
-
-void DialogClientView::CancelWindow() {
-  // Only notify the delegate once. See |delegate_allowed_close_|'s comment.
-  if (!delegate_allowed_close_ && GetDialogDelegate()->Cancel()) {
-    delegate_allowed_close_ = true;
-    GetWidget()->CloseWithReason(
-        views::Widget::ClosedReason::kCancelButtonClicked);
-  }
-}
-
 void DialogClientView::SetButtonRowInsets(const gfx::Insets& insets) {
   button_row_insets_ = insets;
   if (GetWidget())
     UpdateDialogButtons();
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// DialogClientView, ClientView overrides:
-
-bool DialogClientView::CanClose() {
-  // If the dialog is closing but no Accept or Cancel action has been performed
-  // before, it's a Close action.
-  if (!delegate_allowed_close_)
-    delegate_allowed_close_ = GetDialogDelegate()->Close();
-  return delegate_allowed_close_;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // DialogClientView, View overrides:
 
@@ -235,9 +206,9 @@
     return;
 
   if (sender == ok_button_)
-    AcceptWindow();
+    GetDialogDelegate()->AcceptDialog();
   else if (sender == cancel_button_)
-    CancelWindow();
+    GetDialogDelegate()->CancelDialog();
   else
     NOTREACHED();
 }
diff --git a/ui/views/window/dialog_client_view.h b/ui/views/window/dialog_client_view.h
index d7bc090..faf6b5e 100644
--- a/ui/views/window/dialog_client_view.h
+++ b/ui/views/window/dialog_client_view.h
@@ -43,10 +43,6 @@
   DialogClientView(Widget* widget, View* contents_view);
   ~DialogClientView() override;
 
-  // Accept or Cancel the dialog.
-  void AcceptWindow();
-  void CancelWindow();
-
   // Accessors in case the user wishes to adjust these buttons.
   LabelButton* ok_button() const { return ok_button_; }
   LabelButton* cancel_button() const { return cancel_button_; }
@@ -54,9 +50,6 @@
 
   void SetButtonRowInsets(const gfx::Insets& insets);
 
-  // ClientView implementation:
-  bool CanClose() override;
-
   // View implementation:
   gfx::Size CalculatePreferredSize() const override;
   gfx::Size GetMinimumSize() const override;
@@ -136,12 +129,6 @@
   // Container view for the button row.
   ButtonRowContainer* button_row_container_ = nullptr;
 
-  // True if we've notified the delegate the window is closing and the delegate
-  // allowed the close. In some situations it's possible to get two closes (see
-  // http://crbug.com/71940). This is used to avoid notifying the delegate
-  // twice, which can have bad consequences.
-  bool delegate_allowed_close_ = false;
-
   // Used to prevent unnecessary or potentially harmful changes during
   // SetupLayout(). Everything will be manually updated afterwards.
   bool adding_or_removing_views_ = false;
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index bc9c10c..caf94e2 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -137,52 +137,22 @@
 }
 
 bool DialogDelegate::Cancel() {
+  DCHECK(!already_started_close_);
   if (cancel_callback_)
     RunCloseCallback(std::move(cancel_callback_));
   return true;
 }
 
 bool DialogDelegate::Accept() {
+  DCHECK(!already_started_close_);
   if (accept_callback_)
     RunCloseCallback(std::move(accept_callback_));
   return true;
 }
 
-bool DialogDelegate::Close() {
-  // Test for callback_delivered_ here, because it indicates that:
-  // 1) A new-style callback used to be present in one of these slots
-  // 2) It has already been invoked and therefore the slot is now empty
-  if (callback_delivered_ || close_callback_ || cancel_callback_ ||
-      accept_callback_) {
-    if (close_callback_)
-      RunCloseCallback(std::move(close_callback_));
-    return true;
-  } else {
-    return DefaultClose();
-  }
-}
-
-bool DialogDelegate::DefaultClose() {
-  DCHECK(!close_callback_);
-  DCHECK(!cancel_callback_);
-  DCHECK(!accept_callback_);
-  int buttons = GetDialogButtons();
-  if ((buttons & ui::DIALOG_BUTTON_CANCEL) ||
-      (buttons == ui::DIALOG_BUTTON_NONE)) {
-    return Cancel();
-  }
-  return Accept();
-}
-
 void DialogDelegate::RunCloseCallback(base::OnceClosure callback) {
-  // TODO(ellyjones): It would be better if this instead was:
-  //   DCHECK(!callback_delivered_);
-  // i.e., it was enforced by Views that client code does not cause the
-  // DialogDelegate to be closed from within a closure handler. Unfortunately a
-  // lot of client code does not behave that way right now.
-  if (callback_delivered_)
-    return;
-  callback_delivered_ = true;
+  DCHECK(!already_started_close_);
+  already_started_close_ = true;
   std::move(callback).Run();
 }
 
@@ -223,6 +193,36 @@
   return WidgetDelegate::CreateNonClientFrameView(widget);
 }
 
+void DialogDelegate::WindowWillClose() {
+  if (already_started_close_)
+    return;
+
+  bool new_callback_present =
+      close_callback_ || cancel_callback_ || accept_callback_;
+
+  if (close_callback_)
+    RunCloseCallback(std::move(close_callback_));
+
+  if (new_callback_present)
+    return;
+
+  // Old-style close behavior: if the only button was Ok, call Accept();
+  // otherwise call Cancel(). Note that in this case the window is already going
+  // to close, so the return values of Accept()/Cancel(), which normally say
+  // whether the window should close, are ignored.
+  int buttons = GetDialogButtons();
+  if (buttons == ui::DIALOG_BUTTON_OK)
+    Accept();
+  else
+    Cancel();
+
+  // This is set here instead of before the invocations of Accept()/Cancel() so
+  // that those methods can DCHECK that !already_started_close_. Otherwise,
+  // client code could (eg) call Accept() from inside the cancel callback, which
+  // could lead to multiple callbacks being delivered from this class.
+  already_started_close_ = true;
+}
+
 // static
 NonClientFrameView* DialogDelegate::CreateDialogFrameView(Widget* widget) {
   LayoutProvider* provider = LayoutProvider::Get();
@@ -325,12 +325,9 @@
   return std::move(extra_view_);
 }
 
-void DialogDelegate::CancelDialog() {
-  GetDialogClientView()->CancelWindow();
-}
-
-void DialogDelegate::AcceptDialog() {
-  GetDialogClientView()->AcceptWindow();
+bool DialogDelegate::Close() {
+  WindowWillClose();
+  return true;
 }
 
 void DialogDelegate::ResetViewShownTimeStampForTesting() {
@@ -341,6 +338,24 @@
   GetDialogClientView()->SetButtonRowInsets(insets);
 }
 
+void DialogDelegate::AcceptDialog() {
+  if (already_started_close_ || !Accept())
+    return;
+
+  already_started_close_ = true;
+  GetWidget()->CloseWithReason(
+      views::Widget::ClosedReason::kAcceptButtonClicked);
+}
+
+void DialogDelegate::CancelDialog() {
+  if (already_started_close_ || !Cancel())
+    return;
+
+  already_started_close_ = true;
+  GetWidget()->CloseWithReason(
+      views::Widget::ClosedReason::kCancelButtonClicked);
+}
+
 DialogDelegate::~DialogDelegate() {
   UMA_HISTOGRAM_LONG_TIMES("Dialog.DialogDelegate.Duration",
                            base::TimeTicks::Now() - creation_time_);
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index 2f5c4ff..c4e3da8 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -129,6 +129,7 @@
   DialogDelegate* AsDialogDelegate() override;
   ClientView* CreateClientView(Widget* widget) override;
   NonClientFrameView* CreateNonClientFrameView(Widget* widget) override;
+  void WindowWillClose() override;
 
   static NonClientFrameView* CreateDialogFrameView(Widget* widget);
 
@@ -225,16 +226,19 @@
   //    lazy layout system in View::InvalidateLayout
   std::unique_ptr<View> DisownExtraView();
 
-  // Externally or accept the dialog. These methods:
+  // Accept or cancel the dialog, as though the user had pressed the
+  // Accept/Cancel buttons. These methods:
   // 1) Invoke the DialogDelegate's Cancel or Accept methods
   // 2) Depending on their return value, close the dialog's widget.
   // Neither of these methods can be called before the dialog has been
   // initialized.
-  void CancelDialog();
   void AcceptDialog();
+  void CancelDialog();
 
-  // Deprecated, for compatibility with a few remaining unit tests.
-  // TODO(https://crbug.com/1011446): Delete this method.
+  // This method invokes the behavior that *would* happen if this dialog's
+  // containing widget were closed. It is present only as a compatibility shim
+  // for unit tests; do not add new calls to it.
+  // TODO(https://crbug.com/1011446): Delete this.
   bool Close();
 
   // Reset the dialog's shown timestamp, for tests that are subject to the
@@ -269,10 +273,6 @@
   const DialogClientView* GetDialogClientView() const;
   DialogClientView* GetDialogClientView();
 
-  // Implements the default close behavior, as described in the comment on
-  // Close() above.
-  bool DefaultClose();
-
   // Runs a close callback, ensuring that at most one close callback is ever
   // run.
   void RunCloseCallback(base::OnceClosure callback);
@@ -303,8 +303,9 @@
   base::OnceClosure cancel_callback_;
   base::OnceClosure close_callback_;
 
-  // Whether any of the three callbacks just above has been delivered yet.
-  bool callback_delivered_ = false;
+  // Whether any of the three callbacks just above has been delivered yet, *or*
+  // one of the Accept/Cancel methods have been called and returned true.
+  bool already_started_close_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(DialogDelegate);
 };
diff --git a/ui/views/window/dialog_delegate_unittest.cc b/ui/views/window/dialog_delegate_unittest.cc
index 82f4fd8..ace06841 100644
--- a/ui/views/window/dialog_delegate_unittest.cc
+++ b/ui/views/window/dialog_delegate_unittest.cc
@@ -17,6 +17,7 @@
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/style/platform_style.h"
 #include "ui/views/test/views_test_base.h"
+#include "ui/views/test/widget_test.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_delegate.h"
 
@@ -466,4 +467,41 @@
   EXPECT_FALSE(closed);
 }
 
+class TestDialogDelegateView : public DialogDelegateView {
+ public:
+  TestDialogDelegateView(bool* accepted, bool* cancelled)
+      : accepted_(accepted), cancelled_(cancelled) {}
+  ~TestDialogDelegateView() override = default;
+
+ private:
+  bool Accept() override {
+    *(accepted_) = true;
+    return true;
+  }
+  bool Cancel() override {
+    *(cancelled_) = true;
+    return true;
+  }
+
+  bool* accepted_;
+  bool* cancelled_;
+};
+
+TEST_F(DialogDelegateCloseTest, OldClosePathDoesNotDoubleClose) {
+  bool accepted = false;
+  bool cancelled = false;
+
+  auto* dialog = new TestDialogDelegateView(&accepted, &cancelled);
+  Widget* widget =
+      DialogDelegate::CreateDialogWidget(dialog, GetContext(), nullptr);
+  widget->Show();
+
+  views::test::WidgetDestroyedWaiter destroyed_waiter(widget);
+  dialog->AcceptDialog();
+  destroyed_waiter.Wait();
+
+  EXPECT_TRUE(accepted);
+  EXPECT_FALSE(cancelled);
+}
+
 }  // namespace views