diff --git a/DEPS b/DEPS
index 4b86f91..b614df8 100644
--- a/DEPS
+++ b/DEPS
@@ -43,6 +43,8 @@
   'checkout_nacl',
   'checkout_oculus_sdk',
   'checkout_openxr',
+  'cros_boards',
+  'cros_boards_with_qemu_images',
   'mac_xcode_version',
 ]
 
@@ -154,11 +156,14 @@
 
   # Default to the empty board. Desktop Chrome OS builds don't need cros SDK
   # dependencies. Other Chrome OS builds should always define this explicitly.
-  'cros_boards': '',
+  'cros_boards': Str(''),
+  'cros_boards_with_qemu_images': Str(''),
   # Building for CrOS is only supported on linux currently.
   'checkout_simplechrome': '(checkout_chromeos and host_os == "linux") and ("{cros_boards}" != "")',
   # Surround the board var in quotes so gclient doesn't try parsing the string
   # as an expression.
+  # TODO(crbug.com/937821): Replace uses of this var with
+  # 'cros_boards_with_qemu_images' above.
   'cros_download_vm': '(("{cros_boards}" == "amd64-generic") or ("{cros_boards}" == "betty")) or ("{cros_boards}" == "betty-pi-arc")',
   # Should we build and test for public (ie: full) CrOS images, or private
   # (ie: release) images.
@@ -194,7 +199,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '7fe0359130f9ede30e1bcf4befc23ec352d48f42',
+  'skia_revision': 'd72cb4c076b7113b667a706ddecb8f03695ca555',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -206,7 +211,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '82359f926f81b3ffd795d65799295dfc7b7adc4d',
+  'angle_revision': '45f54928a6280936f6a2f60aa7a413fb68292604',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -257,7 +262,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '71892ba52d17e5a441c60b9a546764cb29165276',
+  'catapult_revision': '19856e664354b32ed421601e7723480c3b51c8cd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -317,7 +322,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'c15c6eb9d8c37754c189d72b0f4add60cc499bd8',
+  'dawn_revision': 'e8e089ad282e27d21c7bb426a73819d67266a3b3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -356,7 +361,7 @@
   'ukey2_revision': '0275885d8e6038c39b8a8ca55e75d1d4d1727f47',
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'tint_revision': '919011af0a2ae1b663aae0aaa4083a3f9f13e66d',
+  'tint_revision': '5f43fedcddba8fd4cc3d9ff21b2ae8c5f8f4f4bc',
 
   # TODO(crbug.com/941824): The values below need to be kept in sync
   # between //DEPS and //buildtools/DEPS, so if you're updating one,
@@ -894,7 +899,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '78a6bf63710df0fefc27b268bc63a03ac37d9220',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '233589f2e73f9cc1e4c759d93ab0565efc81ad36',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1247,7 +1252,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'cab02df7a5c58d33c19afef6a84194b65f0aa826',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'df50009d3edec39cee9a7f5a59851701848fa32d',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1325,7 +1330,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': '2mJq_oWu-q0HyRGke_W6FjEJNmcrTYhhhN1jjxIg9_4C'
+              'version': 'qH1GinMX67CP0YPwoB1xzpQVlBVVjqbDaNokFsY7Ax8C'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1475,7 +1480,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '820021d24600b8628b8644c43fddf67fa37ccf84',
+    Var('webrtc_git') + '/src.git' + '@' + '0c5f244817c22303d65811e6a25b3d97f8a08127',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1513,7 +1518,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/linux-amd64',
-          'version': 'git_revision:03ea4eb574acd232e223a6b13d15ebfb61f1c0d8',
+          'version': 'git_revision:98b51cb50bea650bac90ab1001ea509dcb6eac2f',
         },
       ],
       'dep_type': 'cipd',
@@ -1523,7 +1528,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'git_revision:03ea4eb574acd232e223a6b13d15ebfb61f1c0d8',
+          'version': 'git_revision:98b51cb50bea650bac90ab1001ea509dcb6eac2f',
         },
       ],
       'dep_type': 'cipd',
@@ -1533,7 +1538,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'git_revision:03ea4eb574acd232e223a6b13d15ebfb61f1c0d8',
+          'version': 'git_revision:98b51cb50bea650bac90ab1001ea509dcb6eac2f',
         },
       ],
       'dep_type': 'cipd',
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
index ca6aacb..28eba7c5 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
@@ -62,6 +62,7 @@
 import org.chromium.android_webview.AwContentsStatics;
 import org.chromium.android_webview.AwPrintDocumentAdapter;
 import org.chromium.android_webview.AwSettings;
+import org.chromium.android_webview.AwThreadUtils;
 import org.chromium.android_webview.gfx.AwDrawFnImpl;
 import org.chromium.android_webview.renderer_priority.RendererPriority;
 import org.chromium.base.BuildInfo;
@@ -441,12 +442,7 @@
     private void checkThread() {
         if (!ThreadUtils.runningOnUiThread()) {
             final RuntimeException threadViolation = createThreadException();
-            PostTask.postTask(UiThreadTaskTraits.DEFAULT, new Runnable() {
-                @Override
-                public void run() {
-                    throw threadViolation;
-                }
-            });
+            AwThreadUtils.postToUiThreadLooper(() -> { throw threadViolation; });
             throw createThreadException();
         }
     }
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
index 5763a6e..eddedc3 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -27,6 +27,7 @@
 import org.chromium.android_webview.AwNetworkChangeNotifierRegistrationPolicy;
 import org.chromium.android_webview.AwProxyController;
 import org.chromium.android_webview.AwServiceWorkerController;
+import org.chromium.android_webview.AwThreadUtils;
 import org.chromium.android_webview.AwTracingController;
 import org.chromium.android_webview.HttpAuthDatabase;
 import org.chromium.android_webview.ProductConfig;
@@ -283,7 +284,7 @@
 
         // We must post to the UI thread to cover the case that the user has invoked Chromium
         // startup by using the (thread-safe) CookieManager rather than creating a WebView.
-        PostTask.postTask(UiThreadTaskTraits.DEFAULT, new Runnable() {
+        AwThreadUtils.postToUiThreadLooper(new Runnable() {
             @Override
             public void run() {
                 synchronized (mLock) {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 9e1d09d2..3dd9051 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -3275,7 +3275,7 @@
     @CalledByNative
     private static void generateMHTMLCallback(String path, long size, Callback<String> callback) {
         if (callback == null) return;
-        callback.onResult(size < 0 ? null : path);
+        AwThreadUtils.postToUiThreadLooper(() -> { callback.onResult(size < 0 ? null : path); });
     }
 
     @CalledByNative
@@ -3358,7 +3358,7 @@
         if (isDestroyed(NO_WARN)) return;
         // Posting avoids invoking the callback inside invoking_composite_
         // (see synchronous_compositor_impl.cc and crbug/452530).
-        PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> callback.onComplete(requestId));
+        AwThreadUtils.postToUiThreadLooper(() -> callback.onComplete(requestId));
     }
 
     // Called as a result of AwContentsJni.get().updateLastHitTestData.
@@ -3544,7 +3544,7 @@
         if (path == null || isDestroyed(WARN)) {
             if (callback == null) return;
 
-            PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, callback.bind(null));
+            AwThreadUtils.postToUiThreadLooper(callback.bind(null));
         } else {
             AwContentsJni.get().generateMHTML(mNativeAwContents, AwContents.this, path, callback);
         }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwGeolocationPermissions.java b/android_webview/java/src/org/chromium/android_webview/AwGeolocationPermissions.java
index 55d617c..86facf50 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwGeolocationPermissions.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwGeolocationPermissions.java
@@ -6,8 +6,6 @@
 
 import android.content.SharedPreferences;
 
-import org.chromium.base.task.PostTask;
-import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.net.GURLUtils;
 
 import java.util.HashSet;
@@ -101,7 +99,7 @@
      */
     public void getAllowed(String origin, final org.chromium.base.Callback<Boolean> callback) {
         final boolean finalAllowed = isOriginAllowed(origin);
-        PostTask.postTask(UiThreadTaskTraits.DEFAULT, callback.bind(finalAllowed));
+        AwThreadUtils.postToUiThreadLooper(callback.bind(finalAllowed));
     }
 
     /**
@@ -114,7 +112,7 @@
                 origins.add(name.substring(PREF_PREFIX.length()));
             }
         }
-        PostTask.postTask(UiThreadTaskTraits.DEFAULT, callback.bind(origins));
+        AwThreadUtils.postToUiThreadLooper(callback.bind(origins));
     }
 
     /**
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 590c8710..35df6143 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -266,6 +266,8 @@
     "dbus/liveness_service_provider.h",
     "dbus/url_handler_service_provider.cc",
     "dbus/url_handler_service_provider.h",
+    "dbus/user_authentication_service_provider.cc",
+    "dbus/user_authentication_service_provider.h",
     "debug.cc",
     "debug.h",
     "detachable_base/detachable_base_handler.cc",
@@ -1807,6 +1809,7 @@
     "dbus/org.chromium.GesturePropertiesService.conf",
     "dbus/org.chromium.LivenessService.conf",
     "dbus/org.chromium.UrlHandlerService.conf",
+    "dbus/org.chromium.UserAuthenticationService.conf",
   ]
   output_conf_file = "$root_out_dir/dbus/ash_dbus_services.conf"
   outputs = [ output_conf_file ]
diff --git a/ash/ambient/backdrop/ambient_backend_controller_impl.cc b/ash/ambient/backdrop/ambient_backend_controller_impl.cc
index b4895df1..4969039 100644
--- a/ash/ambient/backdrop/ambient_backend_controller_impl.cc
+++ b/ash/ambient/backdrop/ambient_backend_controller_impl.cc
@@ -199,6 +199,16 @@
       weak_factory_.GetWeakPtr(), settings, std::move(callback)));
 }
 
+void AmbientBackendControllerImpl::FetchSettingPreview(
+    int preview_width,
+    int preview_height,
+    OnSettingPreviewFetchedCallback callback) {
+  Shell::Get()->ambient_controller()->RequestAccessToken(
+      base::BindOnce(&AmbientBackendControllerImpl::FetchSettingPreviewInternal,
+                     weak_factory_.GetWeakPtr(), preview_width, preview_height,
+                     std::move(callback)));
+}
+
 void AmbientBackendControllerImpl::FetchPersonalAlbums(
     int banner_width,
     int banner_height,
@@ -337,6 +347,47 @@
   std::move(callback).Run(success);
 }
 
+void AmbientBackendControllerImpl::FetchSettingPreviewInternal(
+    int preview_width,
+    int preview_height,
+    OnSettingPreviewFetchedCallback callback,
+    const std::string& gaia_id,
+    const std::string& access_token) {
+  if (gaia_id.empty() || access_token.empty()) {
+    LOG(ERROR) << "Failed to fetch access token";
+    // Returns a dummy instance to indicate the failure.
+    std::move(callback).Run(/*preview_urls=*/{});
+    return;
+  }
+
+  BackdropClientConfig::Request request =
+      backdrop_client_config_.CreateFetchSettingPreviewRequest(
+          preview_width, preview_height, gaia_id, access_token);
+  std::unique_ptr<network::ResourceRequest> resource_request =
+      CreateResourceRequest(request);
+  auto backdrop_url_loader = std::make_unique<BackdropURLLoader>();
+  auto* loader_ptr = backdrop_url_loader.get();
+  loader_ptr->Start(
+      std::move(resource_request), /*request_body=*/base::nullopt,
+      NO_TRAFFIC_ANNOTATION_YET,
+      base::BindOnce(&AmbientBackendControllerImpl::OnSettingPreviewFetched,
+                     weak_factory_.GetWeakPtr(), std::move(callback),
+                     std::move(backdrop_url_loader)));
+}
+
+void AmbientBackendControllerImpl::OnSettingPreviewFetched(
+    OnSettingPreviewFetchedCallback callback,
+    std::unique_ptr<BackdropURLLoader> backdrop_url_loader,
+    std::unique_ptr<std::string> response) {
+  DCHECK(backdrop_url_loader);
+
+  // Parse the |SettingPreviewResponse| out from the response string.
+  // Note that the |preview_urls| can be empty if the parsing has failed.
+  std::vector<std::string> preview_urls =
+      BackdropClientConfig::ParseSettingPreviewResponse(*response);
+  std::move(callback).Run(std::move(preview_urls));
+}
+
 void AmbientBackendControllerImpl::FetchPersonalAlbumsInternal(
     int banner_width,
     int banner_height,
diff --git a/ash/ambient/backdrop/ambient_backend_controller_impl.h b/ash/ambient/backdrop/ambient_backend_controller_impl.h
index 23512c5a..41585d12 100644
--- a/ash/ambient/backdrop/ambient_backend_controller_impl.h
+++ b/ash/ambient/backdrop/ambient_backend_controller_impl.h
@@ -31,6 +31,9 @@
   void GetSettings(GetSettingsCallback callback) override;
   void UpdateSettings(const AmbientSettings& settings,
                       UpdateSettingsCallback callback) override;
+  void FetchSettingPreview(int preview_width,
+                           int preview_height,
+                           OnSettingPreviewFetchedCallback callback) override;
   void FetchPersonalAlbums(int banner_width,
                            int banner_height,
                            int num_albums,
@@ -69,6 +72,17 @@
                         std::unique_ptr<BackdropURLLoader> backdrop_url_loader,
                         std::unique_ptr<std::string> response);
 
+  void FetchSettingPreviewInternal(int preview_width,
+                                   int preview_height,
+                                   OnSettingPreviewFetchedCallback callback,
+                                   const std::string& gaia_id,
+                                   const std::string& access_token);
+
+  void OnSettingPreviewFetched(
+      OnSettingPreviewFetchedCallback callback,
+      std::unique_ptr<BackdropURLLoader> backdrop_url_loader,
+      std::unique_ptr<std::string> response);
+
   void FetchPersonalAlbumsInternal(int banner_width,
                                    int banner_height,
                                    int num_albums,
diff --git a/ash/ambient/fake_ambient_backend_controller_impl.cc b/ash/ambient/fake_ambient_backend_controller_impl.cc
index 9da9b9a..0b6e1cd 100644
--- a/ash/ambient/fake_ambient_backend_controller_impl.cc
+++ b/ash/ambient/fake_ambient_backend_controller_impl.cc
@@ -60,6 +60,16 @@
       FROM_HERE, base::BindOnce(std::move(callback), /*success=*/true));
 }
 
+void FakeAmbientBackendControllerImpl::FetchSettingPreview(
+    int preview_width,
+    int preview_height,
+    OnSettingPreviewFetchedCallback callback) {
+  std::vector<std::string> urls = {kFakeUrl};
+  // Pretend to respond asynchronously.
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), urls));
+}
+
 void FakeAmbientBackendControllerImpl::FetchPersonalAlbums(
     int banner_width,
     int banner_height,
diff --git a/ash/ambient/fake_ambient_backend_controller_impl.h b/ash/ambient/fake_ambient_backend_controller_impl.h
index 9be4fec3..fe9f728 100644
--- a/ash/ambient/fake_ambient_backend_controller_impl.h
+++ b/ash/ambient/fake_ambient_backend_controller_impl.h
@@ -25,6 +25,9 @@
   void GetSettings(GetSettingsCallback callback) override;
   void UpdateSettings(const AmbientSettings& settings,
                       UpdateSettingsCallback callback) override;
+  void FetchSettingPreview(int preview_width,
+                           int preview_height,
+                           OnSettingPreviewFetchedCallback) override;
   void FetchPersonalAlbums(int banner_width,
                            int banner_height,
                            int num_albums,
diff --git a/ash/dbus/ash_dbus_services.cc b/ash/dbus/ash_dbus_services.cc
index 028f8ab..2d528c1d 100644
--- a/ash/dbus/ash_dbus_services.cc
+++ b/ash/dbus/ash_dbus_services.cc
@@ -8,6 +8,7 @@
 #include "ash/dbus/gesture_properties_service_provider.h"
 #include "ash/dbus/liveness_service_provider.h"
 #include "ash/dbus/url_handler_service_provider.h"
+#include "ash/dbus/user_authentication_service_provider.h"
 #include "base/feature_list.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/dbus/services/cros_dbus_service.h"
@@ -40,6 +41,11 @@
       dbus::ObjectPath(chromeos::kUrlHandlerServicePath),
       chromeos::CrosDBusService::CreateServiceProviderList(
           std::make_unique<UrlHandlerServiceProvider>()));
+  user_authentication_service_ = chromeos::CrosDBusService::Create(
+      system_bus, chromeos::kUserAuthenticationServiceName,
+      dbus::ObjectPath(chromeos::kUserAuthenticationServicePath),
+      chromeos::CrosDBusService::CreateServiceProviderList(
+          std::make_unique<UserAuthenticationServiceProvider>()));
 }
 
 AshDBusServices::~AshDBusServices() {
@@ -47,6 +53,7 @@
   gesture_properties_service_.reset();
   liveness_service_.reset();
   url_handler_service_.reset();
+  user_authentication_service_.reset();
 }
 
 }  // namespace ash
diff --git a/ash/dbus/ash_dbus_services.h b/ash/dbus/ash_dbus_services.h
index e92486a..d787ebe 100644
--- a/ash/dbus/ash_dbus_services.h
+++ b/ash/dbus/ash_dbus_services.h
@@ -30,6 +30,7 @@
   std::unique_ptr<chromeos::CrosDBusService> gesture_properties_service_;
   std::unique_ptr<chromeos::CrosDBusService> liveness_service_;
   std::unique_ptr<chromeos::CrosDBusService> url_handler_service_;
+  std::unique_ptr<chromeos::CrosDBusService> user_authentication_service_;
 
   DISALLOW_COPY_AND_ASSIGN(AshDBusServices);
 };
diff --git a/ash/dbus/org.chromium.UserAuthenticationService.conf b/ash/dbus/org.chromium.UserAuthenticationService.conf
new file mode 100644
index 0000000..0fa18f1
--- /dev/null
+++ b/ash/dbus/org.chromium.UserAuthenticationService.conf
@@ -0,0 +1,20 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+  "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<!--
+  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.
+-->
+<busconfig>
+  <policy user="chronos">
+    <allow own="org.chromium.UserAuthenticationService"/>
+  </policy>
+
+  <!--
+    u2fd uses this service to ask Chrome to show the auth dialog for WebAuthn.
+  -->
+  <policy user="u2f">
+    <allow send_destination="org.chromium.UserAuthenticationService"
+           send_interface="org.chromium.UserAuthenticationServiceInterface"/>
+  </policy>
+</busconfig>
diff --git a/ash/dbus/user_authentication_service_provider.cc b/ash/dbus/user_authentication_service_provider.cc
new file mode 100644
index 0000000..9a6320c
--- /dev/null
+++ b/ash/dbus/user_authentication_service_provider.cc
@@ -0,0 +1,48 @@
+// 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 "ash/dbus/user_authentication_service_provider.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace ash {
+
+UserAuthenticationServiceProvider::UserAuthenticationServiceProvider() =
+    default;
+
+UserAuthenticationServiceProvider::~UserAuthenticationServiceProvider() =
+    default;
+
+void UserAuthenticationServiceProvider::Start(
+    scoped_refptr<dbus::ExportedObject> exported_object) {
+  exported_object->ExportMethod(
+      chromeos::kUserAuthenticationServiceInterface,
+      chromeos::kUserAuthenticationServiceShowAuthDialogMethod,
+      base::BindRepeating(&UserAuthenticationServiceProvider::ShowAuthDialog,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&UserAuthenticationServiceProvider::OnExported,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void UserAuthenticationServiceProvider::OnExported(
+    const std::string& interface_name,
+    const std::string& method_name,
+    bool success) {
+  if (!success) {
+    LOG(ERROR) << "Failed to export " << interface_name << "." << method_name;
+  }
+}
+
+void UserAuthenticationServiceProvider::ShowAuthDialog(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  // TODO(yichengli): Call AuthDialogController to start authentication flow.
+  std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
+}
+
+}  // namespace ash
diff --git a/ash/dbus/user_authentication_service_provider.h b/ash/dbus/user_authentication_service_provider.h
new file mode 100644
index 0000000..a35bb5d
--- /dev/null
+++ b/ash/dbus/user_authentication_service_provider.h
@@ -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.
+
+#ifndef ASH_DBUS_USER_AUTHENTICATION_SERVICE_PROVIDER_H_
+#define ASH_DBUS_USER_AUTHENTICATION_SERVICE_PROVIDER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/services/cros_dbus_service.h"
+#include "dbus/exported_object.h"
+
+namespace dbus {
+class MethodCall;
+}
+
+namespace ash {
+
+// This class exports a D-Bus method that platform daemons call to request Ash
+// to start in-session user authentication flow.
+class UserAuthenticationServiceProvider
+    : public chromeos::CrosDBusService::ServiceProviderInterface {
+ public:
+  UserAuthenticationServiceProvider();
+  UserAuthenticationServiceProvider(const UserAuthenticationServiceProvider&) =
+      delete;
+  UserAuthenticationServiceProvider& operator=(
+      const UserAuthenticationServiceProvider&) = delete;
+  ~UserAuthenticationServiceProvider() override;
+
+  // CrosDBusService::ServiceProviderInterface overrides:
+  void Start(scoped_refptr<dbus::ExportedObject> exported_object) override;
+
+ private:
+  // Called from ExportedObject when a handler is exported as a D-Bus
+  // method or failed to be exported.
+  void OnExported(const std::string& interface_name,
+                  const std::string& method_name,
+                  bool success);
+
+  // Called on UI thread in response to D-Bus requests.
+  void ShowAuthDialog(dbus::MethodCall* method_call,
+                      dbus::ExportedObject::ResponseSender response_sender);
+
+  // Keep this last so that all weak pointers will be invalidated at the
+  // beginning of destruction.
+  base::WeakPtrFactory<UserAuthenticationServiceProvider> weak_ptr_factory_{
+      this};
+};
+
+}  // namespace ash
+
+#endif  // ASH_DBUS_USER_AUTHENTICATION_SERVICE_PROVIDER_H_
diff --git a/ash/multi_profile_uma.cc b/ash/multi_profile_uma.cc
index bfd33e0..56c44b17 100644
--- a/ash/multi_profile_uma.cc
+++ b/ash/multi_profile_uma.cc
@@ -9,12 +9,6 @@
 namespace ash {
 
 // static
-void MultiProfileUMA::RecordSigninUser(SigninUserAction action) {
-  UMA_HISTOGRAM_ENUMERATION("MultiProfile.SigninUserUIPath", action,
-                            NUM_SIGNIN_USER_ACTIONS);
-}
-
-// static
 void MultiProfileUMA::RecordSwitchActiveUser(SwitchActiveUserAction action) {
   UMA_HISTOGRAM_ENUMERATION("MultiProfile.SwitchActiveUserUIPath", action,
                             NUM_SWITCH_ACTIVE_USER_ACTIONS);
diff --git a/ash/multi_profile_uma.h b/ash/multi_profile_uma.h
index b488487..ad4be7b 100644
--- a/ash/multi_profile_uma.h
+++ b/ash/multi_profile_uma.h
@@ -16,22 +16,12 @@
 class ASH_EXPORT MultiProfileUMA {
  public:
   // Used for UMA metrics. Do not reorder.
-  enum SigninUserAction {
-    SIGNIN_USER_BY_TRAY = 0,
-    SIGNIN_USER_BY_BROWSER_FRAME,  // Deprecated.
-    NUM_SIGNIN_USER_ACTIONS
-  };
-
-  // Used for UMA metrics. Do not reorder.
   enum SwitchActiveUserAction {
     SWITCH_ACTIVE_USER_BY_TRAY = 0,
     SWITCH_ACTIVE_USER_BY_ACCELERATOR,
     NUM_SWITCH_ACTIVE_USER_ACTIONS
   };
 
-  // Record signing in a new user and what UI path was taken.
-  static void RecordSigninUser(SigninUserAction action);
-
   // Record switching the active user and what UI path was taken.
   static void RecordSwitchActiveUser(SwitchActiveUserAction action);
 
diff --git a/ash/public/cpp/ambient/ambient_backend_controller.h b/ash/public/cpp/ambient/ambient_backend_controller.h
index 535a0f3..d45bff24 100644
--- a/ash/public/cpp/ambient/ambient_backend_controller.h
+++ b/ash/public/cpp/ambient/ambient_backend_controller.h
@@ -80,6 +80,8 @@
   using GetSettingsCallback =
       base::OnceCallback<void(const base::Optional<AmbientSettings>& settings)>;
   using UpdateSettingsCallback = base::OnceCallback<void(bool success)>;
+  using OnSettingPreviewFetchedCallback =
+      base::OnceCallback<void(const std::vector<std::string>& preview_urls)>;
   using OnPersonalAlbumsFetchedCallback =
       base::OnceCallback<void(PersonalAlbums)>;
 
@@ -106,6 +108,11 @@
   virtual void UpdateSettings(const AmbientSettings& settings,
                               UpdateSettingsCallback callback) = 0;
 
+  // Fetch preview images for live album.
+  virtual void FetchSettingPreview(int preview_width,
+                                   int preview_height,
+                                   OnSettingPreviewFetchedCallback) = 0;
+
   virtual void FetchPersonalAlbums(int banner_width,
                                    int banner_height,
                                    int num_albums,
diff --git a/ash/system/unified/user_chooser_detailed_view_controller.cc b/ash/system/unified/user_chooser_detailed_view_controller.cc
index 0b003d0..7fe4c860 100644
--- a/ash/system/unified/user_chooser_detailed_view_controller.cc
+++ b/ash/system/unified/user_chooser_detailed_view_controller.cc
@@ -65,7 +65,6 @@
 }
 
 void UserChooserDetailedViewController::HandleAddUserAction() {
-  MultiProfileUMA::RecordSigninUser(MultiProfileUMA::SIGNIN_USER_BY_TRAY);
   tray_controller_->CloseBubble();
   Shell::Get()->session_controller()->ShowMultiProfileLogin();
   // ShowMultiProfileLogin may delete us.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index b2b136b..19b9983 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3624,7 +3624,9 @@
       ":jni_java",
       "//base/test:test_support_java",
       "//third_party/android_deps:androidx_test_monitor_java",
+      "//third_party/android_deps:androidx_test_runner_java",
       "//third_party/android_deps:androidx_test_uiautomator_uiautomator_java",
+      "//third_party/android_support_test_runner:rules_java",
       "//third_party/android_support_test_runner:runner_java",
       "//third_party/hamcrest:hamcrest_java",
       "//third_party/junit:junit",
@@ -3836,6 +3838,8 @@
       ":base_junit_test_support",
       ":jni_java",
       "//base/test:test_support_java",
+      "//third_party/android_deps:androidx_annotation_annotation_java",
+      "//third_party/android_deps:androidx_test_runner_java",
       "//third_party/hamcrest:hamcrest_java",
     ]
   }
diff --git a/build/.gitignore b/build/.gitignore
index d0208526..a4a36d9 100644
--- a/build/.gitignore
+++ b/build/.gitignore
@@ -12,6 +12,7 @@
 /goma
 /gomacc.lock
 /ipch/
+/lacros/prebuilt_ash_chrome/
 /Release
 /Release_x64
 /win_toolchain.json
diff --git a/build/android/docs/build_config.md b/build/android/docs/build_config.md
index 9f69aaa..8a301c8 100644
--- a/build/android/docs/build_config.md
+++ b/build/android/docs/build_config.md
@@ -67,7 +67,6 @@
       "resource_zips/ui/android/ui_strings_grd.resources.zip"
     ],
     "extra_package_names": [],
-    "extra_r_text_files": []
   }
 }
 ```
@@ -130,7 +129,6 @@
     --aapt-path ../../third_party/android_sdk/public/build-tools/29.0.2/aapt \
     --dependencies-res-zips=@FileArg\(gen/ui/android/ui_java_resources.build_config:resources:dependency_zips\) \
     --extra-res-packages=@FileArg\(gen/ui/android/ui_java_resources.build_config:resources:extra_package_names\) \
-    --extra-r-text-files=@FileArg\(gen/ui/android/ui_java_resources.build_config:resources:extra_r_text_files\) \
     --resource-dirs=\[\"../../ui/android/java/res\"\] \
     --debuggable \
     --resource-zip-out resource_zips/ui/android/ui_java_resources.resources.zip \
diff --git a/build/android/gyp/compile_resources.py b/build/android/gyp/compile_resources.py
index eece2eb3..e57510a 100755
--- a/build/android/gyp/compile_resources.py
+++ b/build/android/gyp/compile_resources.py
@@ -1117,9 +1117,9 @@
     logging.debug('Creating R.srcjar')
     resource_utils.CreateRJavaFiles(
         build.srcjar_dir, package_for_library, build.r_txt_path,
-        options.extra_res_packages, options.extra_r_text_files,
-        rjava_build_options, options.srcjar_out, custom_root_package_name,
-        grandparent_custom_package_name, options.extra_main_r_text_files)
+        options.extra_res_packages, rjava_build_options, options.srcjar_out,
+        custom_root_package_name, grandparent_custom_package_name,
+        options.extra_main_r_text_files)
     build_utils.ZipDir(build.srcjar_path, build.srcjar_dir)
 
     # Sanity check that the created resources have the expected package ID.
@@ -1151,9 +1151,8 @@
   if options.only_verify_expectations:
     return
 
-  depfile_deps = (
-      options.dependencies_res_zips + options.extra_main_r_text_files +
-      options.extra_r_text_files + options.include_resources)
+  depfile_deps = (options.dependencies_res_zips +
+                  options.extra_main_r_text_files + options.include_resources)
 
   possible_input_paths = depfile_deps + [
       options.aapt2_path,
diff --git a/build/android/gyp/create_r_java.py b/build/android/gyp/create_r_java.py
index dfb697ec..6ef7a98 100755
--- a/build/android/gyp/create_r_java.py
+++ b/build/android/gyp/create_r_java.py
@@ -26,7 +26,6 @@
                                     package_name,
                                     rtxt_out,
                                     extra_res_packages=[],
-                                    extra_r_txt_files=[],
                                     rjava_build_options=rjava_build_options,
                                     srcjar_out=srcjar_out)
     build_utils.ZipDir(srcjar_out, build.srcjar_dir)
diff --git a/build/android/gyp/prepare_resources.py b/build/android/gyp/prepare_resources.py
index dd5d1e4..3e17f26 100755
--- a/build/android/gyp/prepare_resources.py
+++ b/build/android/gyp/prepare_resources.py
@@ -195,9 +195,9 @@
 
         # Not passing in custom_root_package_name or parent to keep
         # file names unique.
-        resource_utils.CreateRJavaFiles(
-            build.srcjar_dir, package, r_txt_path, options.extra_res_packages,
-            options.extra_r_text_files, rjava_build_options, options.srcjar_out)
+        resource_utils.CreateRJavaFiles(build.srcjar_dir, package, r_txt_path,
+                                        options.extra_res_packages,
+                                        rjava_build_options, options.srcjar_out)
 
       build_utils.ZipDir(options.srcjar_out, build.srcjar_dir)
 
@@ -236,7 +236,6 @@
   possible_input_paths += options.include_resources
   input_paths = [x for x in possible_input_paths if x]
   input_paths.extend(options.dependencies_res_zips)
-  input_paths.extend(options.extra_r_text_files)
 
   # Resource files aren't explicitly listed in GN. Listing them in the depfile
   # ensures the target will be marked stale when resource files are removed.
diff --git a/build/android/gyp/util/resource_utils.py b/build/android/gyp/util/resource_utils.py
index 17a1eb0..4fdad8a29 100644
--- a/build/android/gyp/util/resource_utils.py
+++ b/build/android/gyp/util/resource_utils.py
@@ -495,7 +495,6 @@
                      package,
                      main_r_txt_file,
                      extra_res_packages,
-                     extra_r_txt_files,
                      rjava_build_options,
                      srcjar_out,
                      custom_root_package_name=None,
@@ -510,9 +509,6 @@
     main_r_txt_file: The main R.txt file containing the valid values
       of _all_ resource IDs.
     extra_res_packages: A list of extra package names.
-    extra_r_txt_files: A list of extra R.txt files. One per item in
-      |extra_res_packages|. Note that all resource IDs in them will be ignored,
-      |and replaced by the values extracted from |main_r_txt_file|.
     rjava_build_options: An RJavaBuildOptions instance that controls how
       exactly the R.java file is generated.
     srcjar_out: Path of desired output srcjar.
@@ -527,18 +523,14 @@
   Raises:
     Exception if a package name appears several times in |extra_res_packages|
   """
-  assert len(extra_res_packages) == len(extra_r_txt_files), \
-         'Need one R.txt file per package'
   rjava_build_options._MaybeRewriteRTxtPackageIds(main_r_txt_file)
 
   packages = list(extra_res_packages)
-  r_txt_files = list(extra_r_txt_files)
 
   if package and package not in packages:
     # Sometimes, an apk target and a resources target share the same
     # AndroidManifest.xml and thus |package| will already be in |packages|.
     packages.append(package)
-    r_txt_files.append(main_r_txt_file)
 
   # Map of (resource_type, name) -> Entry.
   # Contains the correct values for resources.
@@ -580,53 +572,19 @@
   with open(root_r_java_path, 'w') as f:
     f.write(root_java_file_contents)
 
-  # Map of package_name->resource_type->entry
-  resources_by_package = (
-      collections.defaultdict(lambda: collections.defaultdict(list)))
-  # Build the R.java files using each package's R.txt file, but replacing
-  # each entry's placeholder value with correct values from all_resources.
-  for package, r_txt_file in zip(packages, r_txt_files):
-    if package in resources_by_package:
-      raise Exception(('Package name "%s" appeared twice. All '
-                       'android_resources() targets must use unique package '
-                       'names, or no package name at all.') % package)
-    resources_by_type = resources_by_package[package]
-    # The sub-R.txt files have the wrong values at this point. Read them to
-    # figure out which entries belong to them, but use the values from the
-    # main R.txt file.
-    for entry in _ParseTextSymbolsFile(r_txt_file):
-      entry = all_resources.get((entry.resource_type, entry.name))
-      # For most cases missing entry here is an error. It means that some
-      # library claims to have or depend on a resource that isn't included into
-      # the APK. There is one notable exception: Google Play Services (GMS).
-      # GMS is shipped as a bunch of AARs. One of them - basement - contains
-      # R.txt with ids of all resources, but most of the resources are in the
-      # other AARs. However, all other AARs reference their resources via
-      # basement's R.java so the latter must contain all ids that are in its
-      # R.txt. Most targets depend on only a subset of GMS AARs so some
-      # resources are missing, which is okay because the code that references
-      # them is missing too. We can't get an id for a resource that isn't here
-      # so the only solution is to skip the resource entry entirely.
-      #
-      # We can verify that all entries referenced in the code were generated
-      # correctly by running Proguard on the APK: it will report missing
-      # fields.
-      if entry:
-        resources_by_type[entry.resource_type].append(entry)
-
-  for package, resources_by_type in resources_by_package.iteritems():
+  for package in packages:
     _CreateRJavaSourceFile(srcjar_dir, package, root_r_java_package,
-                           resources_by_type, rjava_build_options)
+                           rjava_build_options)
 
 
 def _CreateRJavaSourceFile(srcjar_dir, package, root_r_java_package,
-                           resources_by_type, rjava_build_options):
+                           rjava_build_options):
   """Generates an R.java source file."""
   package_r_java_dir = os.path.join(srcjar_dir, *package.split('.'))
   build_utils.MakeDirectory(package_r_java_dir)
   package_r_java_path = os.path.join(package_r_java_dir, 'R.java')
-  java_file_contents = _RenderRJavaSource(
-      package, root_r_java_package, resources_by_type, rjava_build_options)
+  java_file_contents = _RenderRJavaSource(package, root_r_java_package,
+                                          rjava_build_options)
   with open(package_r_java_path, 'w') as f:
     f.write(java_file_contents)
 
@@ -646,8 +604,7 @@
   return len(res_ids)
 
 
-def _RenderRJavaSource(package, root_r_java_package, resources_by_type,
-                       rjava_build_options):
+def _RenderRJavaSource(package, root_r_java_package, rjava_build_options):
   """Generates the contents of a R.java file."""
   template = Template(
       """/* AUTO-GENERATED FILE.  DO NOT MODIFY. */
@@ -671,7 +628,6 @@
 
   return template.render(
       package=package,
-      resources=resources_by_type,
       resource_types=sorted(_ALL_RESOURCE_TYPES),
       root_package=root_r_java_package,
       has_on_resources_loaded=rjava_build_options.has_on_resources_loaded)
@@ -958,12 +914,6 @@
       '--extra-res-packages',
       help='Additional package names to generate R.java files for.')
 
-  input_opts.add_argument(
-      '--extra-r-text-files',
-      help='For each additional package, the R.txt file should contain a '
-           'list of resources to be included in the R.java file in the format '
-           'generated by aapt.')
-
   return (parser, input_opts, output_opts)
 
 
@@ -990,12 +940,6 @@
   else:
     options.extra_res_packages = []
 
-  if options.extra_r_text_files:
-    options.extra_r_text_files = (
-        build_utils.ParseGnList(options.extra_r_text_files))
-  else:
-    options.extra_r_text_files = []
-
 
 def ParseAndroidResourceStringsFromXml(xml_data):
   """Parse and Android xml resource file and extract strings from it.
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 09bc9219..94bed333 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -129,10 +129,9 @@
 dependencies for the current target. Computed automatically by
 `write_build_config.py`.
 
-* `deps_info['extra_r_text_files']`:
-Always empty for `android_resources` types. Otherwise, the list of
-`deps_info['r_text']` entries for all `android_resources` dependencies for
-the current target. Computed automatically.
+* `deps_info['dependency_r_txt_files']`:
+Exists only on dist_aar. It is the list of deps_info['r_text_path'] from
+transitive dependencies. Computed automatically.
 
 
 # `.build_config` target types description:
@@ -174,7 +173,7 @@
 Optional. Path to the top-level Android manifest file associated with these
 resources (if not provided, an empty manifest will be used to generate R.txt).
 
-* `deps_info['r_text']`:
+* `deps_info['r_text_path']`:
 Provide the path to the `R.txt` file that describes the resources wrapped by
 this target. Normally this file is generated from the content of the resource
 directories or zip file, but some targets can provide their own `R.txt` file
@@ -182,8 +181,8 @@
 
 * `deps_info['srcjar_path']`:
 Path to the `.srcjar` file that contains the auto-generated `R.java` source
-file corresponding to the content of `deps_info['r_text']`. This is *always*
-generated from the content of `deps_info['r_text']` by the
+file corresponding to the content of `deps_info['r_text_path']`. This is
+*always* generated from the content of `deps_info['r_text_path']` by the
 `build/android/gyp/process_resources.py` script.
 
 * `deps_info['static_library_dependent_classpath_configs']`:
@@ -1390,24 +1389,18 @@
         c['resources_zip'] for c in all_resources_deps if c['resources_zip']
     ]
     extra_package_names = []
-    extra_r_text_files = []
 
     if options.type != 'android_resources':
       extra_package_names = [
           c['package_name'] for c in all_resources_deps if 'package_name' in c
       ]
-      extra_r_text_files = [
-          c['r_text_path'] for c in all_resources_deps if 'r_text_path' in c
-      ]
       # In final types (i.e. apks and modules) that create real R.java files,
-      # they must collect package names and rtxt from java_libraries as well.
+      # they must collect package names from java_libraries as well.
       # https://crbug.com/1073476
       if options.type != 'java_library':
         extra_package_names.extend([
             c['package_name'] for c in all_library_deps if 'package_name' in c
         ])
-        extra_r_text_files.extend(
-            [c['r_text_path'] for c in all_library_deps if 'r_text_path' in c])
 
 
     # For feature modules, remove any resources that already exist in the base
@@ -1421,14 +1414,9 @@
           c for c in extra_package_names if c not in
           base_module_build_config['deps_info']['extra_package_names']
       ]
-      extra_r_text_files = [
-          c for c in extra_r_text_files if c not in
-          base_module_build_config['deps_info']['extra_r_text_files']
-      ]
 
     config['deps_info']['dependency_zips'] = dependency_zips
     config['deps_info']['extra_package_names'] = extra_package_names
-    config['deps_info']['extra_r_text_files'] = extra_r_text_files
     if options.type == 'android_apk' and options.tested_apk_config:
       config['deps_info']['arsc_package_name'] = (
           tested_apk_config['package_name'])
@@ -1442,6 +1430,14 @@
           options.extra_classpath_jars)
       deps_info['extra_classpath_jars'] = extra_classpath_jars
 
+  if options.type == 'dist_aar':
+    # dist_aar combines all dependency R.txt files into one for the aar.
+    r_text_files = [
+        c['r_text_path'] for c in all_resources_deps + all_library_deps
+        if 'r_text_path' in c
+    ]
+    deps_info['dependency_r_txt_files'] = r_text_files
+
   if is_java_target:
     # The classpath used to compile this target when annotation processors are
     # present.
@@ -1619,9 +1615,6 @@
       # The srcjars containing the generated R.java files are excluded for APK
       # targets the use static libraries, so we add them here to ensure the
       # union of resource IDs are available in the static library APK.
-      for r_text in base_config['extra_r_text_files']:
-        if r_text not in extra_r_text_files:
-          extra_r_text_files.append(r_text)
       for package in base_config['extra_package_names']:
         if package not in extra_package_names:
           extra_package_names.append(package)
diff --git a/build/args/chromeos/README.md b/build/args/chromeos/README.md
index 941aab8..e02e185 100644
--- a/build/args/chromeos/README.md
+++ b/build/args/chromeos/README.md
@@ -3,9 +3,12 @@
 Files in this directory are populated by running `gclient sync` with specific
 arguments set in the .gclient file. Specifically:
 * The file must have a top-level variable set: `target_os = ["chromeos"]`
-* The `"custom_vars"` parameter of the chromium/src.git solution must include the
-  parameter: `"cros_boards": "{BOARD_NAMES}"` where `{BOARD_NAMES}` is a
+* The `"custom_vars"` parameter of the chromium/src.git solution must include
+  the parameter: `"cros_boards": "{BOARD_NAMES}"` where `{BOARD_NAMES}` is a
   colon-separated list of boards you'd like to checkout.
+* If you'd like to a checkout a QEMU-bootable image for a given board, include
+  it in the `cros_boards_with_qemu_images` var rather than the `cros_boards`
+  var.
 
 A typical .gclient file is a sibling of the src/ directory, and might look like
 this:
@@ -18,7 +21,10 @@
     "custom_deps": {},
     "custom_vars" : {
         "checkout_src_internal": True,
-        "cros_boards": "eve",
+        "cros_boards": "eve:kevin",
+        # If a QEMU-bootable image is desired for any board, move it from
+        # the previous var to the following:
+        "cros_boards_with_qemu_images": "amd64-generic",
     },
   },
 ]
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index a8b77e94..3340f336 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -2112,7 +2112,6 @@
         "--include-resources=@FileArg($_rebased_build_config:android:sdk_jars)",
         "--dependencies-res-zips=@FileArg($_rebased_build_config:deps_info:dependency_zips)",
         "--extra-res-packages=@FileArg($_rebased_build_config:deps_info:extra_package_names)",
-        "--extra-r-text-files=@FileArg($_rebased_build_config:deps_info:extra_r_text_files)",
         "--res-sources-path=$_rebased_res_sources_path",
         "--resource-zip-out",
         rebase_path(invoker.resources_zip, root_build_dir),
@@ -2329,7 +2328,6 @@
       rebase_path(android_sdk_tools_bundle_aapt2, root_build_dir),
       "--dependencies-res-zips=@FileArg($_rebased_build_config:deps_info:dependency_zips)",
       "--extra-res-packages=@FileArg($_rebased_build_config:deps_info:extra_package_names)",
-      "--extra-r-text-files=@FileArg($_rebased_build_config:deps_info:extra_r_text_files)",
       "--extra-main-r-text-files=@FileArg($_rebased_build_config:deps_info:extra_main_r_text_files)",
       "--min-sdk-version=${invoker.min_sdk_version}",
       "--target-sdk-version=${invoker.target_sdk_version}",
@@ -3036,14 +3034,9 @@
           filter_exclude(
               [ get_label_info(":$target_name", "label_no_toolchain") ],
               [
-                "//base*",
                 "//chrome/browser*",
                 "//clank*",
                 "//components*",
-                "//remoting*",
-                "//third_party*",
-                "//ui*",
-                "//weblayer*",
 
                 # This is due to the "special group" bypass.
                 "*_bundle_module__java__header",
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 12a82f8..8e1562a6 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1783,7 +1783,7 @@
         "--output",
         rebase_path(invoker.output, root_build_dir),
         "--dependencies-res-zips=@FileArg($_rebased_build_config:deps_info:dependency_zips)",
-        "--r-text-files=@FileArg($_rebased_build_config:deps_info:extra_r_text_files)",
+        "--r-text-files=@FileArg($_rebased_build_config:deps_info:dependency_r_txt_files)",
         "--proguard-configs=@FileArg($_rebased_build_config:deps_info:proguard_all_configs)",
       ]
       if (_direct_deps_only) {
diff --git a/build/config/ios/rules.gni b/build/config/ios/rules.gni
index ecc34fba..ace2acc 100644
--- a/build/config/ios/rules.gni
+++ b/build/config/ios/rules.gni
@@ -1958,6 +1958,7 @@
     args = [
              "merge",
              "-f=xml1",
+             "-x=$xcode_version",
              "-o=" + rebase_path(_output_name, root_build_dir),
            ] + rebase_path(sources, root_build_dir)
 
diff --git a/build/config/linux/BUILD.gn b/build/config/linux/BUILD.gn
index fda1419..2a5f886 100644
--- a/build/config/linux/BUILD.gn
+++ b/build/config/linux/BUILD.gn
@@ -42,7 +42,6 @@
     "xcb",
     "xcb-dri3",
     "Xcomposite",
-    "Xcursor",
     "Xdamage",
     "Xext",
     "Xfixes",
diff --git a/build/config/mac/plist_util.py b/build/config/mac/plist_util.py
index a7156a62..56e74e6 100644
--- a/build/config/mac/plist_util.py
+++ b/build/config/mac/plist_util.py
@@ -80,7 +80,7 @@
     substitution.
   """
   if isinstance(value, dict):
-      return {k: Interpolate(v, substitutions) for k, v in value.iteritems()}
+    return {k: Interpolate(v, substitutions) for k, v in value.iteritems()}
   if isinstance(value, list):
     return [Interpolate(v, substitutions) for v in value]
   if isinstance(value, str):
@@ -170,6 +170,10 @@
         '-f', '--format', required=True, choices=('xml1', 'binary1', 'json'),
         help='format of the plist file to generate')
     parser.add_argument(
+        '-x',
+        '--xcode-version',
+        help='version of Xcode, ignored (can be used to force rebuild)')
+    parser.add_argument(
           'path', nargs="+",
           help='path to plist files to merge')
 
@@ -201,6 +205,10 @@
     parser.add_argument(
         '-f', '--format', required=True, choices=('xml1', 'binary1', 'json'),
         help='format of the plist file to generate')
+    parser.add_argument(
+        '-x',
+        '--xcode-version',
+        help='version of Xcode, ignored (can be used to force rebuild)')
 
   @staticmethod
   def _Execute(args):
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index ebbb208..aacfec6 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200716.1.1
+0.20200716.2.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index ebbb208..aacfec6 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200716.1.1
+0.20200716.2.1
diff --git a/build/lacros/test_runner.py b/build/lacros/test_runner.py
index 12dabeb1..7b013087 100755
--- a/build/lacros/test_runner.py
+++ b/build/lacros/test_runner.py
@@ -12,8 +12,100 @@
 """
 
 import argparse
+import os
+import shutil
 import subprocess
 import sys
+import tempfile
+import zipfile
+
+_SRC_ROOT = os.path.abspath(
+    os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir))
+sys.path.append(os.path.join(_SRC_ROOT, 'third_party', 'depot_tools'))
+import download_from_google_storage
+
+# Base GS URL to store nightly uploaded official Chrome.
+# TODO(crbug.com/1035562): This URL is created for testing purpose only.
+# Replace this and the following GS paths with real URL and paths once nightly
+# official builds of ash-chrome are uploaded continuously.
+_GS_URL_BASE = 'gs://lacros-testing/desktop-5c0tCh'
+
+# GS path to the file containing the latest version of ash-chrome.
+_GS_ASH_CHROME_LATEST_VERSION_FILE = _GS_URL_BASE + '/latest/linux-chromeos.txt'
+
+# GS path to the zipped ash-chrome build with any given version.
+_GS_ASH_CHROME_PATH = 'linux-chromeos/chrome-linux-chromeos.zip'
+
+# Directory to cache downloaded ash-chrome versions to avoid re-downloading.
+# TODO(crbug.com/1104318): Cleans up unused versions regularly to avoid
+# consuming too much disk space.
+_PREBUILT_ASH_CHROME_DIR = os.path.join(os.path.dirname(__file__),
+                                        'prebuilt_ash_chrome')
+
+
+def _GetAshChromeDirPath(version):
+  """Returns a path to the dir storing the downloaded version of ash-chrome."""
+  return os.path.join(_PREBUILT_ASH_CHROME_DIR, version)
+
+
+def _DownloadAshChromeIfNecessary(version):
+  """Download a given version of ash-chrome if not already exists.
+
+  Args:
+    version: A string representing the version, such as "86.0.4187.0".
+
+  Raises:
+      RuntimeError: If failed to download the specified version, for example,
+          if the version is not present on gcs.
+  """
+
+  def IsAshChromeDirValid(ash_chrome_dir):
+    # This function assumes that once 'chrome' is present, other dependencies
+    # will be present as well, it's not always true, for example, if the test
+    # runner process gets killed in the middle of unzipping (~2 seconds), but
+    # it's unlikely for the assumption to break in practice.
+    return os.path.isdir(ash_chrome_dir) and os.path.isfile(
+        os.path.join(ash_chrome_dir, 'chrome'))
+
+  ash_chrome_dir = _GetAshChromeDirPath(version)
+  if IsAshChromeDirValid(ash_chrome_dir):
+    return
+
+  shutil.rmtree(ash_chrome_dir, ignore_errors=True)
+  os.makedirs(ash_chrome_dir)
+  with tempfile.NamedTemporaryFile() as tmp:
+    gsutil = download_from_google_storage.Gsutil(
+        download_from_google_storage.GSUTIL_DEFAULT_PATH)
+    gs_path = _GS_URL_BASE + '/' + version + '/' + _GS_ASH_CHROME_PATH
+    exit_code = gsutil.call('cp', gs_path, tmp.name)
+    if exit_code:
+      raise RuntimeError('Failed to download: "%s"' % gs_path)
+
+    # https://bugs.python.org/issue15795. ZipFile doesn't preserve permissions.
+    # And in order to workaround the issue, this function is created and used
+    # instead of ZipFile.extractall().
+    # The solution is copied from:
+    # https://stackoverflow.com/questions/42326428/zipfile-in-python-file-permission
+    def ExtractFile(zf, info, extract_dir):
+      zf.extract(info.filename, path=extract_dir)
+      perm = info.external_attr >> 16
+      os.chmod(os.path.join(extract_dir, info.filename), perm)
+
+    with zipfile.ZipFile(tmp.name, 'r') as zf:
+      # Extra all files instead of just 'chrome' binary because 'chrome' needs
+      # other resources and libraries to run.
+      for info in zf.infolist():
+        ExtractFile(zf, info, ash_chrome_dir)
+
+
+def _GetLatestVersionOfAshChrome():
+  """Returns the latest version of uploaded official ash-chrome."""
+  with tempfile.NamedTemporaryFile() as tmp:
+    gsutil = download_from_google_storage.Gsutil(
+        download_from_google_storage.GSUTIL_DEFAULT_PATH)
+    gsutil.check_call('cp', _GS_ASH_CHROME_LATEST_VERSION_FILE, tmp.name)
+    with open(tmp.name, 'r') as f:
+      return f.read().strip()
 
 
 def _ParseArguments():
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index e69264f..ba693df 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -10,7 +10,9 @@
 #include <algorithm>
 #include <cmath>
 #include <limits>
+#include <memory>
 #include <set>
+#include <utility>
 
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
@@ -751,10 +753,12 @@
             layer_tree_impl()->GetMSAASampleCountForRaster(
                 new_display_item_list);
         needs_full_invalidation |=
-            current_display_item_list->discardable_image_map()
-                .contains_only_srgb_images() !=
-            new_display_item_list->discardable_image_map()
-                .contains_only_srgb_images();
+            layer_tree_impl()->GetRasterColorSpace(
+                current_display_item_list->discardable_image_map()
+                    .content_color_usage()) !=
+            layer_tree_impl()->GetRasterColorSpace(
+                new_display_item_list->discardable_image_map()
+                    .content_color_usage());
         if (needs_full_invalidation)
           new_invalidation->Union(gfx::Rect(raster_source->GetSize()));
       }
@@ -1977,17 +1981,10 @@
 
 gfx::ContentColorUsage PictureLayerImpl::GetContentColorUsage() const {
   auto display_item_list = raster_source_->GetDisplayItemList();
-  bool contains_only_srgb_images = true;
-  if (display_item_list) {
-    contains_only_srgb_images =
-        display_item_list->discardable_image_map().contains_only_srgb_images();
-  }
-
-  if (contains_only_srgb_images)
+  if (!display_item_list)
     return gfx::ContentColorUsage::kSRGB;
 
-  // TODO(cblume) This assumes only wide color gamut and not HDR
-  return gfx::ContentColorUsage::kWideColorGamut;
+  return display_item_list->discardable_image_map().content_color_usage();
 }
 
 }  // namespace cc
diff --git a/cc/paint/discardable_image_map.cc b/cc/paint/discardable_image_map.cc
index d90d943e..f417621 100644
--- a/cc/paint/discardable_image_map.cc
+++ b/cc/paint/discardable_image_map.cc
@@ -17,6 +17,7 @@
 #include "cc/paint/paint_filter.h"
 #include "cc/paint/paint_op_buffer.h"
 #include "third_party/skia/include/utils/SkNoDrawCanvas.h"
+#include "ui/gfx/display_color_spaces.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/skia_util.h"
 
@@ -54,9 +55,10 @@
     return std::move(paint_worklet_inputs_);
   }
 
-  bool contains_only_srgb_images() const { return contains_only_srgb_images_; }
+  gfx::ContentColorUsage content_color_usage() const {
+    return content_color_usage_;
+  }
   bool contains_hbd_images() const { return contains_hbd_images_; }
-  bool contains_hdr_images() const { return contains_hdr_images_; }
 
  private:
   class ImageGatheringProvider : public ImageProvider {
@@ -243,10 +245,9 @@
       paint_worklet_inputs_.push_back(std::make_pair(
           paint_image.paint_worklet_input(), paint_image.stable_id()));
     } else {
-      if (!paint_image.isSRGB())
-        contains_only_srgb_images_ = false;
-      if (paint_image.isHDR())
-        contains_hdr_images_ = true;
+      const auto image_color_usage = paint_image.GetContentColorUsage();
+      content_color_usage_ = std::max(content_color_usage_, image_color_usage);
+
       if (paint_image.is_high_bit_depth())
         contains_hbd_images_ = true;
     }
@@ -308,9 +309,8 @@
   base::flat_map<PaintImage::Id, PaintImage::DecodingMode> decoding_mode_map_;
   bool only_gather_animated_images_ = false;
 
-  bool contains_only_srgb_images_ = true;
+  gfx::ContentColorUsage content_color_usage_ = gfx::ContentColorUsage::kSRGB;
   bool contains_hbd_images_ = false;
-  bool contains_hdr_images_ = false;
 };
 
 }  // namespace
@@ -331,9 +331,8 @@
   animated_images_metadata_ = generator.TakeAnimatedImagesMetadata();
   paint_worklet_inputs_ = generator.TakePaintWorkletInputs();
   decoding_mode_map_ = generator.TakeDecodingModeMap();
-  contains_hdr_images_ = generator.contains_hdr_images();
   contains_hbd_images_ = generator.contains_hbd_images();
-  contains_only_srgb_images_ = generator.contains_only_srgb_images();
+  content_color_usage_ = generator.content_color_usage();
   auto images = generator.TakeImages();
   images_rtree_.Build(
       images,
diff --git a/cc/paint/discardable_image_map.h b/cc/paint/discardable_image_map.h
index 846db40..1d5ed40 100644
--- a/cc/paint/discardable_image_map.h
+++ b/cc/paint/discardable_image_map.h
@@ -22,6 +22,7 @@
 #include "cc/paint/paint_worklet_input.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
+#include "ui/gfx/display_color_spaces.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -60,9 +61,10 @@
   void GetDiscardableImagesInRect(const gfx::Rect& rect,
                                   std::vector<const DrawImage*>* images) const;
   const Rects& GetRectsForImage(PaintImage::Id image_id) const;
-  bool contains_hdr_images() const { return contains_hdr_images_; }
+  gfx::ContentColorUsage content_color_usage() const {
+    return content_color_usage_;
+  }
   bool contains_hbd_images() const { return contains_hbd_images_; }
-  bool contains_only_srgb_images() const { return contains_only_srgb_images_; }
   const std::vector<AnimatedImageMetadata>& animated_images_metadata() const {
     return animated_images_metadata_;
   }
@@ -94,9 +96,8 @@
   base::flat_map<PaintImage::Id, Rects> image_id_to_rects_;
   std::vector<AnimatedImageMetadata> animated_images_metadata_;
   base::flat_map<PaintImage::Id, PaintImage::DecodingMode> decoding_mode_map_;
-  bool contains_only_srgb_images_ = true;
+  gfx::ContentColorUsage content_color_usage_ = gfx::ContentColorUsage::kSRGB;
   bool contains_hbd_images_ = false;
-  bool contains_hdr_images_ = false;
 
   RTree<DrawImage> images_rtree_;
 
diff --git a/cc/paint/discardable_image_map_unittest.cc b/cc/paint/discardable_image_map_unittest.cc
index 4dc02b1..92b1fbd 100644
--- a/cc/paint/discardable_image_map_unittest.cc
+++ b/cc/paint/discardable_image_map_unittest.cc
@@ -1132,6 +1132,7 @@
   EXPECT_EQ(ImageRectsToRegion(image_map.GetRectsForImage(image.stable_id())),
             expected_region);
 }
+
 TEST_F(DiscardableImageMapTest, HighBitDepth) {
   gfx::Rect visible_rect(500, 500);
 
@@ -1167,6 +1168,53 @@
   EXPECT_TRUE(image_map2.contains_hbd_images());
 }
 
+TEST_F(DiscardableImageMapTest, ContentColorUsage) {
+  constexpr gfx::Size kSize(25, 25);
+  constexpr gfx::Rect kVisibleRect(500, 500);
+  FakeContentLayerClient content_layer_client;
+  content_layer_client.set_bounds(kVisibleRect.size());
+
+  // Empty map should report a color usage of SRGB.
+  auto display_list = content_layer_client.PaintContentsToDisplayList(
+      ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+  display_list->GenerateDiscardableImagesMetadata();
+  EXPECT_EQ(display_list->discardable_image_map().content_color_usage(),
+            gfx::ContentColorUsage::kSRGB);
+
+  // Adding a SRGB image should remain SRGB.
+  PaintImage discardable_image_srgb = CreateDiscardablePaintImage(
+      kSize, gfx::ColorSpace::CreateSRGB().ToSkColorSpace());
+  content_layer_client.add_draw_image(discardable_image_srgb, gfx::Point(0, 0),
+                                      PaintFlags());
+  display_list = content_layer_client.PaintContentsToDisplayList(
+      ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+  display_list->GenerateDiscardableImagesMetadata();
+  EXPECT_EQ(display_list->discardable_image_map().content_color_usage(),
+            gfx::ContentColorUsage::kSRGB);
+
+  // Adding a WCG image should switch to WCG.
+  PaintImage discardable_image_wcg = CreateDiscardablePaintImage(
+      kSize, gfx::ColorSpace::CreateDisplayP3D65().ToSkColorSpace());
+  content_layer_client.add_draw_image(discardable_image_wcg, gfx::Point(0, 0),
+                                      PaintFlags());
+  display_list = content_layer_client.PaintContentsToDisplayList(
+      ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+  display_list->GenerateDiscardableImagesMetadata();
+  EXPECT_EQ(display_list->discardable_image_map().content_color_usage(),
+            gfx::ContentColorUsage::kWideColorGamut);
+
+  // Adding a HDR image should switch to HDR.
+  PaintImage discardable_image_hdr = CreateDiscardablePaintImage(
+      kSize, gfx::ColorSpace::CreateHDR10().ToSkColorSpace());
+  content_layer_client.add_draw_image(discardable_image_hdr, gfx::Point(0, 0),
+                                      PaintFlags());
+  display_list = content_layer_client.PaintContentsToDisplayList(
+      ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+  display_list->GenerateDiscardableImagesMetadata();
+  EXPECT_EQ(display_list->discardable_image_map().content_color_usage(),
+            gfx::ContentColorUsage::kHDR);
+}
+
 class DiscardableImageMapColorSpaceTest
     : public DiscardableImageMapTest,
       public testing::WithParamInterface<gfx::ColorSpace> {};
@@ -1186,8 +1234,7 @@
   display_list->GenerateDiscardableImagesMetadata();
   const DiscardableImageMap& image_map = display_list->discardable_image_map();
 
-  EXPECT_TRUE(image_map.contains_only_srgb_images());
-  EXPECT_FALSE(image_map.contains_hdr_images());
+  EXPECT_EQ(image_map.content_color_usage(), gfx::ContentColorUsage::kSRGB);
   EXPECT_FALSE(image_map.contains_hbd_images());
 
   content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0),
@@ -1197,14 +1244,16 @@
   display_list->GenerateDiscardableImagesMetadata();
   const DiscardableImageMap& image_map2 = display_list->discardable_image_map();
 
-  if (!image_color_space.IsValid())
-    EXPECT_TRUE(image_map2.contains_only_srgb_images());
-  else if (image_color_space == gfx::ColorSpace::CreateSRGB())
-    EXPECT_TRUE(image_map2.contains_only_srgb_images());
-  else
-    EXPECT_FALSE(image_map2.contains_only_srgb_images());
-
-  EXPECT_EQ(image_color_space.IsHDR(), image_map2.contains_hdr_images());
+  if (!image_color_space.IsValid()) {
+    EXPECT_EQ(image_map2.content_color_usage(), gfx::ContentColorUsage::kSRGB);
+  } else if (image_color_space == gfx::ColorSpace::CreateSRGB()) {
+    EXPECT_EQ(image_map2.content_color_usage(), gfx::ContentColorUsage::kSRGB);
+  } else if (image_color_space.IsHDR()) {
+    EXPECT_EQ(image_map2.content_color_usage(), gfx::ContentColorUsage::kHDR);
+  } else {
+    EXPECT_EQ(image_map2.content_color_usage(),
+              gfx::ContentColorUsage::kWideColorGamut);
+  }
 }
 
 gfx::ColorSpace test_color_spaces[] = {
diff --git a/cc/paint/paint_image.cc b/cc/paint/paint_image.cc
index 955de2c..3ec28bb 100644
--- a/cc/paint/paint_image.cc
+++ b/cc/paint/paint_image.cc
@@ -7,8 +7,10 @@
 #include <memory>
 #include <sstream>
 #include <utility>
+
 #include "base/atomic_sequence_num.h"
 #include "base/hash/hash.h"
+#include "base/logging.h"
 #include "cc/paint/paint_image_builder.h"
 #include "cc/paint/paint_image_generator.h"
 #include "cc/paint/paint_record.h"
@@ -276,28 +278,24 @@
              : GetSkImageInfo().height();
 }
 
-bool PaintImage::isSRGB() const {
+gfx::ContentColorUsage PaintImage::GetContentColorUsage() const {
   // Right now, JS paint worklets can only be in sRGB
   if (paint_worklet_input_)
-    return true;
+    return gfx::ContentColorUsage::kSRGB;
 
-  if (const auto* color_space = GetSkImageInfo().colorSpace())
-    return color_space->isSRGB();
+  const auto* color_space = GetSkImageInfo().colorSpace();
 
   // Assume the image will be sRGB if we don't know yet.
-  return true;
-}
+  if (!color_space || color_space->isSRGB())
+    return gfx::ContentColorUsage::kSRGB;
 
-bool PaintImage::isHDR() const {
-  // Right now, JS paint worklets can only be in sRGB
-  if (paint_worklet_input_)
-    return false;
+  // TODO(crbug.com/1106417): Use SkColorSpace::isHDR() when available.
+  skcms_TransferFunction fn;
+  if (!color_space->isNumericalTransferFn(&fn) && fn.g < 0)
+    return gfx::ContentColorUsage::kHDR;
 
-  if (const auto* color_space = GetSkImageInfo().colorSpace())
-    return gfx::ColorSpace(*color_space).IsHDR();
-
-  // Assume the image will not be HDR if we don't know yet.
-  return false;
+  // If it's not HDR and not SRGB, report it as WCG.
+  return gfx::ContentColorUsage::kWideColorGamut;
 }
 
 const ImageHeaderMetadata* PaintImage::GetImageHeaderMetadata() const {
diff --git a/cc/paint/paint_image.h b/cc/paint/paint_image.h
index 6923d79..2de5f7a 100644
--- a/cc/paint/paint_image.h
+++ b/cc/paint/paint_image.h
@@ -18,6 +18,7 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkYUVAIndex.h"
 #include "third_party/skia/include/core/SkYUVASizeInfo.h"
+#include "ui/gfx/display_color_spaces.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -268,8 +269,8 @@
   SkColorSpace* color_space() const {
     return paint_worklet_input_ ? nullptr : GetSkImageInfo().colorSpace();
   }
-  bool isSRGB() const;
-  bool isHDR() const;
+
+  gfx::ContentColorUsage GetContentColorUsage() const;
 
   // Returns whether this image will be decoded and rendered from YUV data
   // and fills out plane size info, plane index info, and the matrix for
diff --git a/cc/paint/paint_image_unittest.cc b/cc/paint/paint_image_unittest.cc
index 5557efc..d09df35 100644
--- a/cc/paint/paint_image_unittest.cc
+++ b/cc/paint/paint_image_unittest.cc
@@ -27,6 +27,9 @@
                          .set_paint_image_generator(generator)
                          .TakePaintImage();
 
+  // When there's no decoded SkImage the color usage defaults to SRGB.
+  EXPECT_EQ(image.GetContentColorUsage(), gfx::ContentColorUsage::kSRGB);
+
   // The recorded index is 0u but ask for 1u frame.
   SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
   std::vector<size_t> memory(info.computeMinByteSize());
@@ -125,18 +128,34 @@
   EXPECT_TRUE(paint_image.paint_worklet_input());
   EXPECT_EQ(paint_image.width(), size.width());
   EXPECT_EQ(paint_image.height(), size.height());
+  EXPECT_EQ(paint_image.GetContentColorUsage(), gfx::ContentColorUsage::kSRGB);
 }
 
-TEST(PaintImageTest, HbdImage) {
+TEST(PaintImageTest, SrgbImage) {
   auto generator = sk_make_sp<FakePaintImageGenerator>(
-      SkImageInfo::Make(10, 10, kRGBA_F16_SkColorType, kUnknown_SkAlphaType));
+      SkImageInfo::Make(10, 10, kRGBA_F16_SkColorType, kUnknown_SkAlphaType,
+                        gfx::ColorSpace::CreateSRGB().ToSkColorSpace()));
   PaintImage image = PaintImageBuilder::WithDefault()
                          .set_id(PaintImage::GetNextId())
                          .set_paint_image_generator(generator)
                          .set_is_high_bit_depth(true)
                          .TakePaintImage();
   EXPECT_TRUE(image.is_high_bit_depth());
-  EXPECT_FALSE(image.isHDR());
+  EXPECT_EQ(image.GetContentColorUsage(), gfx::ContentColorUsage::kSRGB);
+}
+
+TEST(PaintImageTest, HbdImage) {
+  auto generator = sk_make_sp<FakePaintImageGenerator>(SkImageInfo::Make(
+      10, 10, kRGBA_F16_SkColorType, kUnknown_SkAlphaType,
+      gfx::ColorSpace::CreateDisplayP3D65().ToSkColorSpace()));
+  PaintImage image = PaintImageBuilder::WithDefault()
+                         .set_id(PaintImage::GetNextId())
+                         .set_paint_image_generator(generator)
+                         .set_is_high_bit_depth(true)
+                         .TakePaintImage();
+  EXPECT_TRUE(image.is_high_bit_depth());
+  EXPECT_EQ(image.GetContentColorUsage(),
+            gfx::ContentColorUsage::kWideColorGamut);
 }
 
 TEST(PaintImageTest, PqHdrImage) {
@@ -149,7 +168,7 @@
                          .set_is_high_bit_depth(true)
                          .TakePaintImage();
   EXPECT_TRUE(image.is_high_bit_depth());
-  EXPECT_TRUE(image.isHDR());
+  EXPECT_EQ(image.GetContentColorUsage(), gfx::ContentColorUsage::kHDR);
 }
 
 TEST(PaintImageTest, HlgHdrImage) {
@@ -161,8 +180,9 @@
                          .set_paint_image_generator(generator)
                          .set_is_high_bit_depth(true)
                          .TakePaintImage();
+
   EXPECT_TRUE(image.is_high_bit_depth());
-  EXPECT_TRUE(image.isHDR());
+  EXPECT_EQ(image.GetContentColorUsage(), gfx::ContentColorUsage::kHDR);
 }
 
 }  // namespace cc
diff --git a/cc/scheduler/commit_earlyout_reason.h b/cc/scheduler/commit_earlyout_reason.h
index 4dc8b98..7fe59e4 100644
--- a/cc/scheduler/commit_earlyout_reason.h
+++ b/cc/scheduler/commit_earlyout_reason.h
@@ -11,7 +11,6 @@
 namespace cc {
 
 enum class CommitEarlyOutReason {
-  ABORTED_LAYER_TREE_FRAME_SINK_LOST,
   ABORTED_NOT_VISIBLE,
   ABORTED_DEFERRED_MAIN_FRAME_UPDATE,
   ABORTED_DEFERRED_COMMIT,
@@ -20,8 +19,6 @@
 
 inline const char* CommitEarlyOutReasonToString(CommitEarlyOutReason reason) {
   switch (reason) {
-    case CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST:
-      return "CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST";
     case CommitEarlyOutReason::ABORTED_NOT_VISIBLE:
       return "CommitEarlyOutReason::ABORTED_NOT_VISIBLE";
     case CommitEarlyOutReason::ABORTED_DEFERRED_MAIN_FRAME_UPDATE:
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index 2dcc5e4..42609c9 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -1437,7 +1437,6 @@
   main_thread_missed_last_deadline_ = false;
 
   switch (reason) {
-    case CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST:
     case CommitEarlyOutReason::ABORTED_NOT_VISIBLE:
     case CommitEarlyOutReason::ABORTED_DEFERRED_MAIN_FRAME_UPDATE:
     case CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT:
diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc
index 13fc6d46..66539b4 100644
--- a/cc/scheduler/scheduler_state_machine_unittest.cc
+++ b/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -2239,8 +2239,7 @@
   // Abort the commit, since that is what we expect the main thread to do if the
   // LayerTreeFrameSink was lost due to a synchronous call from the main thread
   // to release the LayerTreeFrameSink.
-  state.BeginMainFrameAborted(
-      CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST);
+  state.BeginMainFrameAborted(CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT);
 
   // The scheduler should begin the LayerTreeFrameSink creation now.
   EXPECT_ACTION_UPDATE_STATE(
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index e4be2b3..4490ede8 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/auto_reset.h"
@@ -3662,7 +3663,7 @@
   client_->Reset();
   scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks());
   scheduler_->BeginMainFrameAborted(
-      CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST);
+      CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT);
   EXPECT_ACTIONS("ScheduledActionBeginLayerTreeFrameSinkCreation");
 }
 
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index f888bb15..39e0e30 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -367,13 +367,10 @@
 
 gfx::ContentColorUsage GetContentColorUsageForPrioritizedTile(
     const PrioritizedTile& prioritized_tile) {
-  // TODO(cblume,ccameron): Add support for HDR.
-  bool contains_only_srgb_images = prioritized_tile.raster_source()
-                                       ->GetDisplayItemList()
-                                       ->discardable_image_map()
-                                       .contains_only_srgb_images();
-  return contains_only_srgb_images ? gfx::ContentColorUsage::kSRGB
-                                   : gfx::ContentColorUsage::kWideColorGamut;
+  return prioritized_tile.raster_source()
+      ->GetDisplayItemList()
+      ->discardable_image_map()
+      .content_color_usage();
 }
 
 }  // namespace
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index c70aa90..5361f07 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -3500,9 +3500,7 @@
                image.GetKeyForFrame(PaintImage::kDefaultFrameIndex).ToString());
   // Optimistically specify the current raster color space, since we assume that
   // it won't change.
-  auto content_color_usage = image.isSRGB()
-                                 ? gfx::ContentColorUsage::kSRGB
-                                 : gfx::ContentColorUsage::kWideColorGamut;
+  auto content_color_usage = image.GetContentColorUsage();
   tile_manager_.decoded_image_tracker().QueueImageDecode(
       image, GetRasterColorSpace(content_color_usage),
       base::BindOnce(&LayerTreeHostImpl::ImageDecodeFinished,
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index b1ed7e4..4b0b9ad 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -2555,6 +2555,11 @@
   return host_impl_->GetMSAASampleCountForRaster(display_list);
 }
 
+gfx::ColorSpace LayerTreeImpl::GetRasterColorSpace(
+    gfx::ContentColorUsage content_color_usage) const {
+  return host_impl_->GetRasterColorSpace(content_color_usage);
+}
+
 void LayerTreeImpl::SetPendingPageScaleAnimation(
     std::unique_ptr<PendingPageScaleAnimation> pending_animation) {
   pending_page_scale_animation_ = std::move(pending_animation);
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index b303eb5..813b40d 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -158,6 +158,8 @@
           decoding_mode_map);
   int GetMSAASampleCountForRaster(
       const scoped_refptr<DisplayItemList>& display_list);
+  gfx::ColorSpace GetRasterColorSpace(
+      gfx::ContentColorUsage content_color_usage) const;
 
   // Tree specific methods exposed to layer-impl tree.
   // ---------------------------------------------------------------------------
diff --git a/chrome/VERSION b/chrome/VERSION
index cd7bd41..7dcbb875 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=86
 MINOR=0
-BUILD=4205
+BUILD=4206
 PATCH=0
diff --git a/chrome/android/feed/OWNERS b/chrome/android/feed/OWNERS
index 27980a9..7336ce41 100644
--- a/chrome/android/feed/OWNERS
+++ b/chrome/android/feed/OWNERS
@@ -1,7 +1,5 @@
-carlosk@chromium.org
-fgorski@chromium.org
-harringtond@chromium.org
-skym@chromium.org
+file://components/feed/OWNERS
+
 twellington@chromium.org
 
 # Team: feed@chromium.org
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/CardMenuBottomSheetContent.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/CardMenuBottomSheetContent.java
index 947fb099..04c3cc9 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/CardMenuBottomSheetContent.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/CardMenuBottomSheetContent.java
@@ -62,7 +62,7 @@
 
     @Override
     public boolean handleBackPress() {
-        return true;
+        return false;
     }
 
     @Override
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java
index ee7b53a4..d6f4d88 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java
@@ -507,8 +507,8 @@
     @Override
     public void downloadLink(String url) {
         RequestCoordinatorBridge.getForProfile(Profile.getLastUsedRegularProfile())
-                .savePageLater(url, OfflinePageBridge.SUGGESTED_ARTICLES_NAMESPACE,
-                        true /* user requested*/);
+                .savePageLater(
+                        url, OfflinePageBridge.NTP_SUGGESTIONS_NAMESPACE, true /* user requested*/);
     }
 
     @Override
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/network_fetch/WPR_RECORD_REPLAY_TESTS.md b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/network_fetch/WPR_RECORD_REPLAY_TESTS.md
index 0662403..a55fe5b 100644
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/network_fetch/WPR_RECORD_REPLAY_TESTS.md
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/network_fetch/WPR_RECORD_REPLAY_TESTS.md
@@ -1,24 +1,61 @@
-# WPR Reocrd/Replay Tests
+# WPR Record/Replay Tests
 
-WPR Reocrd/Replay tests are tests that utilize WPR as a backend to simulate a
+WPR Record/Replay tests are tests that utilize WPR to simulate a
 real backend api service point. When executing a WPR test, there is a Chrome proxy
 session being set up. Inside this Chrome proxy session, there are
 webpagereplay server (aka WPR server), tsProxy server, android forwarder binary,
 and the wiring of these component to each other. A system diagram is available
-[here](https://docs.google.com/document/d/1b6jVuhAuIWh-QRm9brDRfsiBP-eAfuGyDudU1HzkcP8/edit).
+[here](https://docs.google.com/document/d/1xk2ZNGFSQZ8gjc5fCFSck4-WUQehU6GmetMlP-GXYRc/edit)
 
 For a typical WPR Record/Replay test, there are two exeuction modes:
 1. Record mode
 2. Replay mode.
 
+## Update your gclient config
+
+You need to add the following lines to your .gclient checkout.
+
+*  "checkout_src_internal": True,
+*  "checkout_mobile_internal": True,
+
+Here is an example.
+
+```
+solutions = [
+  {
+    "url": "https://chromium.googlesource.com/chromium/src.git",
+    "managed": False,
+    "name": "src",
+    "custom_deps": {},
+    "custom_vars": {
+      "checkout_src_internal": True,
+      "checkout_mobile_internal": True,
+    },
+  },
+]
+target_os = ['android']
+```
+
 ## Mark tests as WPR Record/Replay tests
 
-To mark a test WPR Reocrd/Replay test, there are two annotations to mark on the test method:
+To mark a test WPR Record/Replay test, there are two annotations to mark on the test method:
 1. Features annotation should have 'WPRRecordReplayTest'
 2. WPRArchiveDirectory that has a path points to the wpr archive folder, with
    name typically wpr_tests. It can not be a file.
 
-Here is an [exmaple](https://paste.googleplex.com/6475117775290368).
+Here is an example.
+
+```
+    @Test
+    @MediumTest
+    @Feature({"FeedNewTabPage", "WPRRecordReplayTest", "RenderTest"})
+    @WPRArchiveConfigFilePath("chrome/android/feed/core/javatests/src/org/chromium/chrome/"
+            + "browser/feed/network_fetch/test_data.json")
+    public void
+    launchNTP_withMultipleFeedCardsRendered() throws IOException, InterruptedException {
+    ...
+    }
+```
 
 ## WPR Test file
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/accessibility_tab_switcher/OverviewListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility_tab_switcher/OverviewListLayout.java
index 18025be5..2d04588 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/accessibility_tab_switcher/OverviewListLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility_tab_switcher/OverviewListLayout.java
@@ -58,7 +58,6 @@
                 adjustForFullscreen();
             }
         };
-        mBrowserControlsStateProvider.addObserver(mBrowserControlsObserver);
     }
 
     @Override
@@ -163,10 +162,14 @@
         mTabModelWrapper.setStateBasedOnModel();
 
         doneShowing();
+        mBrowserControlsStateProvider.addObserver(mBrowserControlsObserver);
+        adjustForFullscreen();
     }
 
     @Override
     public void startHiding(int nextId, boolean hintAtTabSelection) {
+        mBrowserControlsStateProvider.removeObserver(mBrowserControlsObserver);
+
         super.startHiding(nextId, hintAtTabSelection);
 
         doneHiding();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
index b3f1c85..31d5726 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
@@ -404,6 +404,8 @@
             @Override
             public void onControlsOffsetChanged(int topOffset, int topControlsMinHeightOffset,
                     int bottomOffset, int bottomControlsMinHeightOffset, boolean needsAnimate) {
+                if (!isActive()) return;
+
                 notifySizeChanged(mWidth, mHeight, mOrientation);
             }
         };
@@ -953,6 +955,16 @@
     }
 
     @Override
+    public void doneShowing() {
+        super.doneShowing();
+
+        if (mBrowserControlsSupplier.get() != null) {
+            mBrowserControlsSupplier.get().addObserver(mBrowserControlsObserver);
+            notifySizeChanged(mWidth, mHeight, mOrientation);
+        }
+    }
+
+    @Override
     public void notifySizeChanged(float width, float height, @Orientation int orientation) {
         mWidth = width;
         mHeight = height;
@@ -1448,6 +1460,10 @@
 
     @Override
     public void startHiding(int nextTabId, boolean hintAtTabSelection) {
+        if (mBrowserControlsSupplier.get() != null) {
+            mBrowserControlsSupplier.get().removeObserver(mBrowserControlsObserver);
+        }
+
         super.startHiding(nextTabId, hintAtTabSelection);
 
         // Reset mIsActiveLayout here instead of in doneHiding() so if a user hits the tab switcher
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareDelegateImpl.java
index 7406b9e1..b9e1b7d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareDelegateImpl.java
@@ -18,7 +18,6 @@
 import org.chromium.chrome.browser.feature_engagement.ScreenshotTabObserver;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
-import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.printing.PrintShareActivity;
 import org.chromium.chrome.browser.printing.TabPrinter;
 import org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfShareActivity;
@@ -296,7 +295,7 @@
                         new ShareSheetCoordinator(controller, tabProvider,
                                 new ShareSheetPropertyModelBuilder(controller,
                                         ContextUtils.getApplicationContext().getPackageManager()),
-                                PrefServiceBridge.getInstance(), printCallback);
+                                printCallback);
                 // TODO(crbug/1009124): open custom share sheet.
                 coordinator.showShareSheet(params, chromeShareExtras, shareStartTime);
             } else {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoDownloadLeakageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoDownloadLeakageTest.java
index fcf888ad..648274d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoDownloadLeakageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoDownloadLeakageTest.java
@@ -67,7 +67,8 @@
 @RunWith(ParameterizedRunner.class)
 @UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 @EnableFeatures({ChromeFeatureList.CCT_INCOGNITO})
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+        "enable-features=UseDownloadOfflineContentProvider"})
 public class IncognitoDownloadLeakageTest {
     private String mDownloadTestPage;
     private final String mDownloadedFileName = "test.gzip";
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 4775064..af0335a8 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7124,6 +7124,14 @@
           desc="Button text for the QR code generator's download button.">
         Download
       </message>
+      <message name="IDS_BROWSER_SHARING_QR_CODE_DIALOG_ERROR_TOO_LONG"
+          desc="Error message displayed when a URL exceeds the limit for which we can generate a QR code.">
+        Use <ph name="CHARACTER_LIMIT">$1<ex>300</ex></ph> characters or fewer
+      </message>
+      <message name="IDS_BROWSER_SHARING_QR_CODE_DIALOG_ERROR_UNKNOWN"
+          desc="Error message displayed when an unknown error occurs.">
+          Can't create QR code
+      </message>
 
       <!-- Sharing features. -->
       <message name="IDS_SHARING_REMOTE_COPY_NOTIFICATION_TITLE_TEXT_CONTENT_UNKNOWN_DEVICE" desc="Title text displayed in a Remote Copy (for text content) notification when the source device name is unknown.">
@@ -9227,6 +9235,12 @@
       <message name="IDS_LIVE_CAPTION_BUBBLE_CLOSE" desc="Tooltip for the Live Caption close button">
         Turn off Live Caption for now
       </message>
+      <message name="IDS_LIVE_CAPTION_BUBBLE_EXPAND" desc="Tooltip for the Live Caption expand button">
+        Show more lines
+      </message>
+      <message name="IDS_LIVE_CAPTION_BUBBLE_COLLAPSE" desc="Tooltip for the Live Caption collapse button">
+        Show fewer lines
+      </message>
       <message name="IDS_LIVE_CAPTION_BUBBLE_ERROR" desc="Error message for the Live Caption bubble when Live Captions are unavailable.">
         Live Caption is not available right now
       </message>
diff --git a/chrome/app/generated_resources_grd/IDS_BROWSER_SHARING_QR_CODE_DIALOG_ERROR_TOO_LONG.png.sha1 b/chrome/app/generated_resources_grd/IDS_BROWSER_SHARING_QR_CODE_DIALOG_ERROR_TOO_LONG.png.sha1
new file mode 100644
index 0000000..09851ba
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_BROWSER_SHARING_QR_CODE_DIALOG_ERROR_TOO_LONG.png.sha1
@@ -0,0 +1 @@
+e6a86d2a91adcbfd929d656b4b9eb7958742eb8f
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_BROWSER_SHARING_QR_CODE_DIALOG_ERROR_UNKNOWN.png.sha1 b/chrome/app/generated_resources_grd/IDS_BROWSER_SHARING_QR_CODE_DIALOG_ERROR_UNKNOWN.png.sha1
new file mode 100644
index 0000000..77222e5
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_BROWSER_SHARING_QR_CODE_DIALOG_ERROR_UNKNOWN.png.sha1
@@ -0,0 +1 @@
+f885dea4c3e525649524067750b177c83f029046
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_LIVE_CAPTION_BUBBLE_COLLAPSE.png.sha1 b/chrome/app/generated_resources_grd/IDS_LIVE_CAPTION_BUBBLE_COLLAPSE.png.sha1
new file mode 100644
index 0000000..49cd9e2e
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_LIVE_CAPTION_BUBBLE_COLLAPSE.png.sha1
@@ -0,0 +1 @@
+b3499faf1b0acf057a63549e44acdbb58e2e5c6c
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_LIVE_CAPTION_BUBBLE_EXPAND.png.sha1 b/chrome/app/generated_resources_grd/IDS_LIVE_CAPTION_BUBBLE_EXPAND.png.sha1
new file mode 100644
index 0000000..88efdf1
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_LIVE_CAPTION_BUBBLE_EXPAND.png.sha1
@@ -0,0 +1 @@
+840b5967d1609a82282fca7e4240361ca3e10030
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings.grdp b/chrome/app/shared_settings_strings.grdp
index 08e933f..f9da5eb 100644
--- a/chrome/app/shared_settings_strings.grdp
+++ b/chrome/app/shared_settings_strings.grdp
@@ -96,6 +96,21 @@
   <message name="IDS_SETTINGS_NEARBY_SHARE_TITLE" desc="Name of the settings page for the Nearby Share feature">
     Nearby Share
   </message>
+  <message name="IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ROW_TITLE" desc="Text for the row in settings which allows a user to set the name of their device which is shown to other devices for the Nearby Share feature.">
+    Device name
+  </message>
+  <message name="IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_DIALOG_TITLE" desc="Title for the dialog which allows a user to set the name of their device which is shown to other devices for the Nearby Share feature.">
+    Device Name
+  </message>
+  <message name="IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_INPUT_LABEL" desc="Label for the input field which allows a user to set the name of their device which is shown to other devices for the Nearby Share feature.">
+    New device name
+  </message>
+  <message name="IDS_SETTINGS_NEARBY_SHARE_EDIT_DEVICE_NAME" desc="Label for the button that opens the dialog which allows a user to set the name of their device which is shown to other devices for the Nearby Share feature.">
+    Change name
+  </message>
+  <message name="IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ARIA_DESCRIPTION" desc="Description for screen readers, not displayed in UI, specifying the current device name which is shown to other devices for the Nearby Share feature.">
+    Current device name is <ph name="DEVICE_NAME">$1<ex>Michael's Chromebook</ex></ph>
+  </message>
 
  <!-- Personalization Options SubPage (strings used by the <settings-personalization-options> element) -->
   <message name="IDS_SETTINGS_ENABLE_URL_KEYED_ANONYMIZED_DATA_COLLECTION" desc="The label of the checkbox to enable/disable url keyed anonymized data collection.">
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ARIA_DESCRIPTION.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ARIA_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..d5ab53d
--- /dev/null
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ARIA_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+b7cf3b1dfec2c0bfe12ddad390708579dc06387c
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_DIALOG_TITLE.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..411e704
--- /dev/null
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+452d7ccf3cb53494cfaf172367a33e9cc216e4eb
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_INPUT_LABEL.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_INPUT_LABEL.png.sha1
new file mode 100644
index 0000000..411e704
--- /dev/null
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_INPUT_LABEL.png.sha1
@@ -0,0 +1 @@
+452d7ccf3cb53494cfaf172367a33e9cc216e4eb
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ROW_TITLE.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ROW_TITLE.png.sha1
new file mode 100644
index 0000000..d5ab53d
--- /dev/null
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ROW_TITLE.png.sha1
@@ -0,0 +1 @@
+b7cf3b1dfec2c0bfe12ddad390708579dc06387c
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_EDIT_DEVICE_NAME.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_EDIT_DEVICE_NAME.png.sha1
new file mode 100644
index 0000000..d5ab53d
--- /dev/null
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_NEARBY_SHARE_EDIT_DEVICE_NAME.png.sha1
@@ -0,0 +1 @@
+b7cf3b1dfec2c0bfe12ddad390708579dc06387c
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4aa6fbe..ad5759f 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3130,6 +3130,8 @@
       "enterprise/reporting/profile_report_generator_desktop.h",
       "enterprise/reporting/report_generator.cc",
       "enterprise/reporting/report_generator.h",
+      "enterprise/reporting/report_generator_desktop.cc",
+      "enterprise/reporting/report_generator_desktop.h",
       "enterprise/reporting/report_scheduler.cc",
       "enterprise/reporting/report_scheduler.h",
       "enterprise/reporting/reporting_delegate_factory_desktop.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4c15e98..dfdf31a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1015,6 +1015,57 @@
         nullptr,
     }};
 
+const FeatureEntry::FeatureVariation
+    kOmniboxRichAutocompletionMinCharVariations[] = {
+        {
+            "Title 0 / Non Prefix 0",
+            (FeatureEntry::FeatureParam[]){
+                {"RichAutocompletionAutocompleteTitlesMinChar", "0"},
+                {"RichAutocompletionAutocompleteNonPrefixMinChar", "0"}},
+            2,
+            nullptr,
+        },
+        {
+            "Title 0 / Non Prefix 3",
+            (FeatureEntry::FeatureParam[]){
+                {"RichAutocompletionAutocompleteTitlesMinChar", "0"},
+                {"RichAutocompletionAutocompleteNonPrefixMinChar", "3"}},
+            2,
+            nullptr,
+        },
+        {
+            "Title 0 / Non Prefix 5",
+            (FeatureEntry::FeatureParam[]){
+                {"RichAutocompletionAutocompleteTitlesMinChar", "0"},
+                {"RichAutocompletionAutocompleteNonPrefixMinChar", "5"}},
+            2,
+            nullptr,
+        },
+        {
+            "Title 3 / Non Prefix 3",
+            (FeatureEntry::FeatureParam[]){
+                {"RichAutocompletionAutocompleteTitlesMinChar", "3"},
+                {"RichAutocompletionAutocompleteNonPrefixMinChar", "3"}},
+            2,
+            nullptr,
+        },
+        {
+            "Title 3 / Non Prefix 5",
+            (FeatureEntry::FeatureParam[]){
+                {"RichAutocompletionAutocompleteTitlesMinChar", "3"},
+                {"RichAutocompletionAutocompleteNonPrefixMinChar", "5"}},
+            2,
+            nullptr,
+        },
+        {
+            "Title 5 / Non Prefix 5",
+            (FeatureEntry::FeatureParam[]){
+                {"RichAutocompletionAutocompleteTitlesMinChar", "5"},
+                {"RichAutocompletionAutocompleteNonPrefixMinChar", "5"}},
+            2,
+            nullptr,
+        }};
+
 #endif  // defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
 
 const FeatureEntry::FeatureParam kOmniboxOnFocusSuggestionsParamSERP[] = {
@@ -3537,6 +3588,13 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(omnibox::kRichAutocompletion,
                                     kOmniboxRichAutocompletionVariations,
                                     "OmniboxBundledExperimentV1")},
+    {"omnibox-rich-autocompletion-min-char",
+     flag_descriptions::kOmniboxRichAutocompletionMinCharName,
+     flag_descriptions::kOmniboxRichAutocompletionMinCharDescription,
+     kOsDesktop,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(omnibox::kRichAutocompletion,
+                                    kOmniboxRichAutocompletionMinCharVariations,
+                                    "OmniboxBundledExperimentV1")},
 #endif  // defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
 
     {"enable-speculative-service-worker-start-on-query-input",
@@ -5888,9 +5946,19 @@
      flag_descriptions::kEditPasswordsInDesktopSettingsName,
      flag_descriptions::kEditPasswordsInDesktopSettingsDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(
-         password_manager::features::kEditPasswordsInDesktopSettings)}
+         password_manager::features::kEditPasswordsInDesktopSettings)},
 #endif  // !defined(OS_ANDROID)
 
+    {"mixed-forms-disable-autofill",
+     flag_descriptions::kMixedFormsDisableAutofillName,
+     flag_descriptions::kMixedFormsDisableAutofillDescription, kOsAll,
+     FEATURE_VALUE_TYPE(autofill::features::kAutofillPreventMixedFormsFilling)},
+
+    {"mixed-forms-interstitial", flag_descriptions::kMixedFormsInterstitialName,
+     flag_descriptions::kMixedFormsInterstitialDescription, kOsAll,
+     FEATURE_VALUE_TYPE(
+         security_interstitials::kInsecureFormSubmissionInterstitial)}
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 81d9c90..2bc798dd 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -865,8 +865,9 @@
 
   // Android's first run is done in Java instead of native.
 #if !defined(OS_ANDROID)
-  process_singleton_.reset(new ChromeProcessSingleton(
-      user_data_dir_, base::Bind(&ProcessSingletonNotificationCallback)));
+  process_singleton_ = std::make_unique<ChromeProcessSingleton>(
+      user_data_dir_,
+      base::BindRepeating(&ProcessSingletonNotificationCallback));
 
   // Cache first run state early.
   first_run::IsChromeFirstRun();
@@ -1317,8 +1318,9 @@
     base::StringToInt(try_chrome, &try_chrome_int);
     TryChromeDialog::Result answer = TryChromeDialog::Show(
         try_chrome_int,
-        base::Bind(&ChromeProcessSingleton::SetModalDialogNotificationHandler,
-                   base::Unretained(process_singleton_.get())));
+        base::BindRepeating(
+            &ChromeProcessSingleton::SetModalDialogNotificationHandler,
+            base::Unretained(process_singleton_.get())));
     switch (answer) {
       case TryChromeDialog::NOT_NOW:
         return chrome::RESULT_CODE_NORMAL_EXIT_CANCEL;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 23bc7dd8..52d0282 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -307,6 +307,7 @@
 #include "content/public/common/web_preferences.h"
 #include "content/public/common/window_container_type.mojom-shared.h"
 #include "device/vr/buildflags/buildflags.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/buildflags/buildflags.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/google_api_keys.h"
diff --git a/chrome/browser/chrome_process_singleton.cc b/chrome/browser/chrome_process_singleton.cc
index e13e262..c0abd23 100644
--- a/chrome/browser/chrome_process_singleton.cc
+++ b/chrome/browser/chrome_process_singleton.cc
@@ -28,7 +28,7 @@
 }
 
 void ChromeProcessSingleton::SetModalDialogNotificationHandler(
-    base::Closure notification_handler) {
+    base::RepeatingClosure notification_handler) {
   modal_dialog_lock_.SetModalDialogNotificationHandler(
       std::move(notification_handler));
 }
diff --git a/chrome/browser/chrome_process_singleton.h b/chrome/browser/chrome_process_singleton.h
index 71df237..4bf0d4fd 100644
--- a/chrome/browser/chrome_process_singleton.h
+++ b/chrome/browser/chrome_process_singleton.h
@@ -44,7 +44,8 @@
 
   // Receives a callback to be run to close the active modal dialog, or an empty
   // closure if the active dialog is dismissed.
-  void SetModalDialogNotificationHandler(base::Closure notification_handler);
+  void SetModalDialogNotificationHandler(
+      base::RepeatingClosure notification_handler);
 
   // Executes previously queued command-line invocations and allows future
   // invocations to be executed immediately.
diff --git a/chrome/browser/chrome_process_singleton_win_unittest.cc b/chrome/browser/chrome_process_singleton_win_unittest.cc
index a87262cd..58f88e6d 100644
--- a/chrome/browser/chrome_process_singleton_win_unittest.cc
+++ b/chrome/browser/chrome_process_singleton_win_unittest.cc
@@ -37,11 +37,11 @@
 
   ChromeProcessSingleton ps1(
       profile_dir.GetPath(),
-      base::Bind(&ServerCallback, base::Unretained(&callback_count)));
+      base::BindRepeating(&ServerCallback, base::Unretained(&callback_count)));
   ps1.Unlock();
 
   ChromeProcessSingleton ps2(profile_dir.GetPath(),
-                             base::Bind(&ClientCallback));
+                             base::BindRepeating(&ClientCallback));
   ps2.Unlock();
 
   ProcessSingleton::NotifyResult result = ps1.NotifyOtherProcessOrCreate();
@@ -63,10 +63,10 @@
 
   ChromeProcessSingleton ps1(
       profile_dir.GetPath(),
-      base::Bind(&ServerCallback, base::Unretained(&callback_count)));
+      base::BindRepeating(&ServerCallback, base::Unretained(&callback_count)));
 
   ChromeProcessSingleton ps2(profile_dir.GetPath(),
-                             base::Bind(&ClientCallback));
+                             base::BindRepeating(&ClientCallback));
   ps2.Unlock();
 
   ProcessSingleton::NotifyResult result = ps1.NotifyOtherProcessOrCreate();
@@ -100,13 +100,13 @@
 
   ChromeProcessSingleton ps1(
       profile_dir.GetPath(),
-      base::Bind(&ServerCallback, base::Unretained(&callback_count)));
-  ps1.SetModalDialogNotificationHandler(
-      base::Bind(&ModalNotificationHandler,
-                 base::Unretained(&called_modal_notification_handler)));
+      base::BindRepeating(&ServerCallback, base::Unretained(&callback_count)));
+  ps1.SetModalDialogNotificationHandler(base::BindRepeating(
+      &ModalNotificationHandler,
+      base::Unretained(&called_modal_notification_handler)));
 
   ChromeProcessSingleton ps2(profile_dir.GetPath(),
-                             base::Bind(&ClientCallback));
+                             base::BindRepeating(&ClientCallback));
   ps2.Unlock();
 
   ProcessSingleton::NotifyResult result = ps1.NotifyOtherProcessOrCreate();
@@ -120,7 +120,7 @@
   ASSERT_TRUE(called_modal_notification_handler);
 
   ASSERT_EQ(0, callback_count);
-  ps1.SetModalDialogNotificationHandler(base::Closure());
+  ps1.SetModalDialogNotificationHandler(base::RepeatingClosure());
   ps1.Unlock();
   // The notifications sent while a modal dialog was open were processed after
   // unlock.
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index dc165d5..9df37f5 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -124,6 +124,7 @@
     "//chromeos/crosapi/mojom",
     "//chromeos/cryptohome",
     "//chromeos/dbus",
+    "//chromeos/dbus:lorgnette_proto",
     "//chromeos/dbus:metrics_event_proto",
     "//chromeos/dbus:oobe_config_proto",
     "//chromeos/dbus:plugin_vm_service_proto",
@@ -2584,6 +2585,8 @@
     # Extension API implementations.
     "extensions/autotest_private/autotest_private_api.cc",
     "extensions/autotest_private/autotest_private_api.h",
+    "extensions/document_scan/document_scan_api.cc",
+    "extensions/document_scan/document_scan_api.h",
     "extensions/echo_private_api.cc",
     "extensions/echo_private_api.h",
     "extensions/file_manager/device_event_router.cc",
@@ -3031,6 +3034,7 @@
     "extensions/default_app_order_unittest.cc",
     "extensions/device_local_account_external_policy_loader_unittest.cc",
     "extensions/device_local_account_management_policy_provider_unittest.cc",
+    "extensions/document_scan/document_scan_api_unittest.cc",
     "extensions/extension_tab_util_delegate_chromeos_unittest.cc",
     "extensions/external_cache_impl_unittest.cc",
     "extensions/file_manager/device_event_router_unittest.cc",
@@ -3188,6 +3192,7 @@
     "net/network_diagnostics/gateway_can_be_pinged_routine_unittest.cc",
     "net/network_diagnostics/has_secure_wifi_connection_routine_unittest.cc",
     "net/network_diagnostics/lan_connectivity_routine_unittest.cc",
+    "net/network_diagnostics/network_diagnostics_impl_unittest.cc",
     "net/network_diagnostics/network_diagnostics_routine_unittest.cc",
     "net/network_diagnostics/signal_strength_routine_unittest.cc",
     "net/network_health/network_health_unittest.cc",
@@ -3479,6 +3484,7 @@
     "//chromeos/components/tether:test_support",
     "//chromeos/constants",
     "//chromeos/cryptohome:test_support",
+    "//chromeos/dbus:lorgnette_proto",
     "//chromeos/dbus:test_support",
     "//chromeos/dbus/authpolicy",
     "//chromeos/dbus/cryptohome",
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_info_data_wrapper.h b/chrome/browser/chromeos/arc/accessibility/accessibility_info_data_wrapper.h
index 5a45f7b..b36fd43 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_info_data_wrapper.h
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_info_data_wrapper.h
@@ -45,7 +45,7 @@
   virtual void PopulateAXRole(ui::AXNodeData* out_data) const = 0;
   virtual void PopulateAXState(ui::AXNodeData* out_data) const = 0;
   virtual void Serialize(ui::AXNodeData* out_data) const = 0;
-  virtual std::string ComputeAXName() const = 0;
+  virtual std::string ComputeAXName(bool do_recursive) const = 0;
   virtual void GetChildren(
       std::vector<AccessibilityInfoDataWrapper*>* children) const = 0;
 
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
index c9bbed2..4fa3c39 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.cc
@@ -92,7 +92,7 @@
 bool AccessibilityNodeInfoDataWrapper::CanBeAccessibilityFocused() const {
   if (!IsAccessibilityFocusableContainer() && !HasAccessibilityFocusableText())
     return false;
-  return !ComputeAXName().empty();
+  return !ComputeAXName(true).empty();
 }
 
 bool AccessibilityNodeInfoDataWrapper::IsAccessibilityFocusableContainer()
@@ -320,7 +320,7 @@
   bool is_node_tree_root = tree_source_->IsRootOfNodeTree(GetId());
 
   // String properties.
-  const std::string name = ComputeAXName();
+  const std::string name = ComputeAXName(true);
   if (!name.empty())
     out_data->SetName(name);
 
@@ -478,9 +478,10 @@
   }
 }
 
-std::string AccessibilityNodeInfoDataWrapper::ComputeAXName() const {
+std::string AccessibilityNodeInfoDataWrapper::ComputeAXName(
+    bool do_recursive) const {
   // Accessible name computation is a concatenated string comprising of:
-  // content description, text, labelled by text, pane title, and cached name
+  // content description, text, labeled by text, pane title, and cached name
   // from previous events.
 
   // TODO(sarakato): Exposing all possible labels for a node, may result in
@@ -492,14 +493,12 @@
   GetProperty(AXStringProperty::CONTENT_DESCRIPTION, &content_description);
   GetProperty(AXStringProperty::TEXT, &text);
 
-  int labelled_by = -1;
-  if (GetProperty(AXIntProperty::LABELED_BY, &labelled_by)) {
-    AccessibilityInfoDataWrapper* labelled_by_node =
-        tree_source_->GetFromId(labelled_by);
-    if (labelled_by_node && labelled_by_node->IsNode()) {
-      // TODO(sarakato): Fix potential bug to be an infinite loop.
-      label = labelled_by_node->ComputeAXName();
-    }
+  int labeled_by = -1;
+  if (do_recursive && GetProperty(AXIntProperty::LABELED_BY, &labeled_by)) {
+    AccessibilityInfoDataWrapper* labeled_by_node =
+        tree_source_->GetFromId(labeled_by);
+    if (labeled_by_node && labeled_by_node->IsNode())
+      label = labeled_by_node->ComputeAXName(false);
   }
 
   std::string pane_title;
@@ -668,7 +667,7 @@
     return;
 
   // Take the name from either content description or text. It's not clear
-  // whether labelled by should be taken into account here.
+  // whether labeled by should be taken into account here.
   std::string name;
   if (!GetProperty(AXStringProperty::CONTENT_DESCRIPTION, &name) ||
       name.empty()) {
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h
index 69dd432..c9bb334 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h
@@ -39,7 +39,7 @@
   void PopulateAXRole(ui::AXNodeData* out_data) const override;
   void PopulateAXState(ui::AXNodeData* out_data) const override;
   void Serialize(ui::AXNodeData* out_data) const override;
-  std::string ComputeAXName() const override;
+  std::string ComputeAXName(bool do_recursive) const override;
   void GetChildren(
       std::vector<AccessibilityInfoDataWrapper*>* children) const override;
 
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc
index 70d22a4..1ac8110 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc
@@ -39,10 +39,8 @@
   arc::SetProperty(node->boolean_properties, prop, value);
 }
 
-void SetProperty(AXNodeInfoData* node,
-                 AXStringProperty prop,
-                 const std::string& value) {
-  arc::SetProperty(node->string_properties, prop, value);
+void SetProperty(AXNodeInfoData* node, AXIntProperty prop, int value) {
+  arc::SetProperty(node->int_properties, prop, value);
 }
 
 void SetProperty(AXNodeInfoData* node,
@@ -51,6 +49,12 @@
   arc::SetProperty(node->int_list_properties, prop, value);
 }
 
+void SetProperty(AXNodeInfoData* node,
+                 AXStringProperty prop,
+                 const std::string& value) {
+  arc::SetProperty(node->string_properties, prop, value);
+}
+
 }  // namespace
 
 class AccessibilityNodeInfoDataWrapperTest : public testing::Test,
@@ -569,4 +573,30 @@
   EXPECT_EQ("state description", value);
 }
 
+TEST_F(AccessibilityNodeInfoDataWrapperTest, LabeledByLoop) {
+  AXNodeInfoData root;
+  root.id = 1;
+  SetProperty(&root, AXIntProperty::LABELED_BY, 2);
+  AccessibilityNodeInfoDataWrapper wrapper(tree_source(), &root);
+  SetIdToWrapper(&wrapper);
+
+  AXNodeInfoData node2;
+  node2.id = 2;
+  AccessibilityNodeInfoDataWrapper child1_wrapper(tree_source(), &node2);
+  SetIdToWrapper(&child1_wrapper);
+  SetProperty(&node2, AXStringProperty::CONTENT_DESCRIPTION, "node2");
+  SetProperty(&node2, AXIntProperty::LABELED_BY, 1);
+
+  ui::AXNodeData data = CallSerialize(wrapper);
+  std::string name;
+  ASSERT_TRUE(
+      data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
+  EXPECT_EQ("node2", name);
+
+  data = CallSerialize(child1_wrapper);
+  ASSERT_TRUE(
+      data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
+  EXPECT_EQ("node2", name);
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.cc b/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.cc
index 2dccabc..f49832d 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.cc
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.cc
@@ -112,7 +112,7 @@
   AccessibilityInfoDataWrapper::Serialize(out_data);
 
   // String properties.
-  const std::string name = ComputeAXName();
+  const std::string name = ComputeAXName(true);
   if (!name.empty()) {
     out_data->SetName(name);
     out_data->SetNameFrom(ax::mojom::NameFrom::kTitle);
@@ -142,7 +142,8 @@
   // and LAYER_ORDER in ax::mojom::IntAttributes.
 }
 
-std::string AccessibilityWindowInfoDataWrapper::ComputeAXName() const {
+std::string AccessibilityWindowInfoDataWrapper::ComputeAXName(
+    bool do_recursive) const {
   std::string title;
   GetProperty(mojom::AccessibilityWindowStringProperty::TITLE, &title);
   return title;
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h b/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h
index 72fd952..5fdbef41 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h
@@ -37,7 +37,7 @@
   void PopulateAXRole(ui::AXNodeData* out_data) const override;
   void PopulateAXState(ui::AXNodeData* out_data) const override;
   void Serialize(ui::AXNodeData* out_data) const override;
-  std::string ComputeAXName() const override;
+  std::string ComputeAXName(bool do_recursive) const override;
   void GetChildren(
       std::vector<AccessibilityInfoDataWrapper*>* children) const override;
 
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
index 02ac56c1..96831b3 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
@@ -518,7 +518,7 @@
       static_cast<AccessibilityNodeInfoDataWrapper*>(node)
           ->set_container_live_status(live_region_type);
 
-      new_live_region_map[node->GetId()] = node->ComputeAXName();
+      new_live_region_map[node->GetId()] = node->ComputeAXName(true);
 
       std::vector<int32_t> children;
       if (GetProperty(node->GetNode()->int_list_properties,
diff --git a/extensions/browser/api/document_scan/DEPS b/chrome/browser/chromeos/extensions/document_scan/DEPS
similarity index 100%
rename from extensions/browser/api/document_scan/DEPS
rename to chrome/browser/chromeos/extensions/document_scan/DEPS
diff --git a/extensions/browser/api/document_scan/document_scan_api.cc b/chrome/browser/chromeos/extensions/document_scan/document_scan_api.cc
similarity index 97%
rename from extensions/browser/api/document_scan/document_scan_api.cc
rename to chrome/browser/chromeos/extensions/document_scan/document_scan_api.cc
index c74d2227..ced29e6 100644
--- a/extensions/browser/api/document_scan/document_scan_api.cc
+++ b/chrome/browser/chromeos/extensions/document_scan/document_scan_api.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "extensions/browser/api/document_scan/document_scan_api.h"
+#include "chrome/browser/chromeos/extensions/document_scan/document_scan_api.h"
 
 #include <utility>
 #include <vector>
diff --git a/extensions/browser/api/document_scan/document_scan_api.h b/chrome/browser/chromeos/extensions/document_scan/document_scan_api.h
similarity index 79%
rename from extensions/browser/api/document_scan/document_scan_api.h
rename to chrome/browser/chromeos/extensions/document_scan/document_scan_api.h
index 66d0140..b36a305 100644
--- a/extensions/browser/api/document_scan/document_scan_api.h
+++ b/chrome/browser/chromeos/extensions/document_scan/document_scan_api.h
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_API_H_
-#define EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_API_H_
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_DOCUMENT_SCAN_DOCUMENT_SCAN_API_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_DOCUMENT_SCAN_DOCUMENT_SCAN_API_H_
 
 #include <memory>
 #include <string>
 
 #include "base/optional.h"
+#include "chrome/common/extensions/api/document_scan.h"
 #include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
 #include "extensions/browser/extension_function.h"
-#include "extensions/common/api/document_scan.h"
 
 namespace extensions {
 
@@ -44,4 +44,4 @@
 
 }  // namespace extensions
 
-#endif  // EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_API_H_
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_DOCUMENT_SCAN_DOCUMENT_SCAN_API_H_
diff --git a/extensions/browser/api/document_scan/document_scan_api_unittest.cc b/chrome/browser/chromeos/extensions/document_scan/document_scan_api_unittest.cc
similarity index 88%
rename from extensions/browser/api/document_scan/document_scan_api_unittest.cc
rename to chrome/browser/chromeos/extensions/document_scan/document_scan_api_unittest.cc
index 9d39f1a..f2f0c83d 100644
--- a/extensions/browser/api/document_scan/document_scan_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/document_scan/document_scan_api_unittest.cc
@@ -8,12 +8,13 @@
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "base/values.h"
+#include "chrome/browser/chromeos/extensions/document_scan/document_scan_api.h"
+#include "chrome/browser/extensions/extension_api_unittest.h"
+#include "chrome/browser/extensions/extension_function_test_utils.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_lorgnette_manager_client.h"
 #include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
-#include "extensions/browser/api/document_scan/document_scan_api.h"
 #include "extensions/browser/api_test_utils.h"
-#include "extensions/browser/api_unittest.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/lorgnette/dbus-constants.h"
@@ -37,21 +38,21 @@
 
 }  // namespace
 
-class DocumentScanScanFunctionTest : public ApiUnitTest {
+class DocumentScanScanFunctionTest : public ExtensionApiUnittest {
  public:
   DocumentScanScanFunctionTest()
       : function_(base::MakeRefCounted<DocumentScanScanFunction>()) {}
   ~DocumentScanScanFunctionTest() override {}
 
   void SetUp() override {
-    ApiUnitTest::SetUp();
+    ExtensionApiUnittest::SetUp();
     chromeos::DBusThreadManager::Initialize();
     function_->set_user_gesture(true);
   }
 
   void TearDown() override {
     chromeos::DBusThreadManager::Shutdown();
-    ApiUnitTest::TearDown();
+    ExtensionApiUnittest::TearDown();
   }
 
   chromeos::FakeLorgnetteManagerClient* GetLorgnetteManagerClient() {
@@ -62,8 +63,9 @@
  protected:
   std::string RunFunctionAndReturnError(const std::string& args) {
     function_->set_extension(extension());
-    std::string error = api_test_utils::RunFunctionAndReturnError(
-        function_.get(), args, browser_context(), api_test_utils::NONE);
+    std::string error =
+        extension_function_test_utils::RunFunctionAndReturnError(
+            function_.get(), args, browser(), api_test_utils::NONE);
     return error;
   }
 
diff --git a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl.cc b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl.cc
index 7b161be..37c19a6 100644
--- a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl.cc
+++ b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl.cc
@@ -5,7 +5,9 @@
 #include "chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl.h"
 
 #include <memory>
+#include <utility>
 
+#include "base/bind.h"
 #include "chrome/browser/chromeos/net/network_diagnostics/dns_latency_routine.h"
 #include "chrome/browser/chromeos/net/network_diagnostics/dns_resolution_routine.h"
 #include "chrome/browser/chromeos/net/network_diagnostics/dns_resolver_present_routine.h"
@@ -13,14 +15,19 @@
 #include "chrome/browser/chromeos/net/network_diagnostics/has_secure_wifi_connection_routine.h"
 #include "chrome/browser/chromeos/net/network_diagnostics/lan_connectivity_routine.h"
 #include "chrome/browser/chromeos/net/network_diagnostics/signal_strength_routine.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
 #include "components/device_event_log/device_event_log.h"
 
 namespace chromeos {
 namespace network_diagnostics {
 
-NetworkDiagnosticsImpl::NetworkDiagnosticsImpl() {}
+NetworkDiagnosticsImpl::NetworkDiagnosticsImpl(
+    chromeos::DebugDaemonClient* debug_daemon_client) {
+  DCHECK(debug_daemon_client);
+  if (debug_daemon_client) {
+    debug_daemon_client_ = debug_daemon_client;
+  }
+}
 
 NetworkDiagnosticsImpl::~NetworkDiagnosticsImpl() {}
 
@@ -31,43 +38,104 @@
 }
 
 void NetworkDiagnosticsImpl::LanConnectivity(LanConnectivityCallback callback) {
-  LanConnectivityRoutine lan_connectivity_routine;
-  lan_connectivity_routine.RunRoutine(std::move(callback));
+  auto routine = std::make_unique<LanConnectivityRoutine>();
+  // RunRoutine() takes a lambda callback that takes ownership of the routine.
+  // This ensures that the routine stays alive when it makes asynchronous mojo
+  // calls. The routine will be destroyed when the lambda exits.
+  routine->RunRoutine(base::BindOnce(
+      [](std::unique_ptr<LanConnectivityRoutine> routine,
+         LanConnectivityCallback callback,
+         mojom::RoutineVerdict verdict) { std::move(callback).Run(verdict); },
+      std::move(routine), std::move(callback)));
 }
 
 void NetworkDiagnosticsImpl::SignalStrength(SignalStrengthCallback callback) {
-  SignalStrengthRoutine signal_strength_routine;
-  signal_strength_routine.RunRoutine(std::move(callback));
+  auto routine = std::make_unique<SignalStrengthRoutine>();
+  // RunRoutine() takes a lambda callback that takes ownership of the routine.
+  // This ensures that the routine stays alive when it makes asynchronous mojo
+  // calls. The routine will be destroyed when the lambda exits.
+  routine->RunRoutine(base::BindOnce(
+      [](std::unique_ptr<SignalStrengthRoutine> routine,
+         SignalStrengthCallback callback, mojom::RoutineVerdict verdict,
+         const std::vector<mojom::SignalStrengthProblem>& problems) {
+        std::move(callback).Run(verdict, std::move(problems));
+      },
+      std::move(routine), std::move(callback)));
 }
 
 void NetworkDiagnosticsImpl::GatewayCanBePinged(
     GatewayCanBePingedCallback callback) {
-  chromeos::DebugDaemonClient* client =
-      chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
-  GatewayCanBePingedRoutine gateway_can_be_pinged_routine(client);
-  gateway_can_be_pinged_routine.RunRoutine(std::move(callback));
+  auto routine =
+      std::make_unique<GatewayCanBePingedRoutine>(debug_daemon_client_);
+  // RunRoutine() takes a lambda callback that takes ownership of the routine.
+  // This ensures that the routine stays alive when it makes asynchronous mojo
+  // calls. The routine will be destroyed when the lambda exits.
+  routine->RunRoutine(base::BindOnce(
+      [](std::unique_ptr<GatewayCanBePingedRoutine> routine,
+         GatewayCanBePingedCallback callback, mojom::RoutineVerdict verdict,
+         const std::vector<mojom::GatewayCanBePingedProblem>& problems) {
+        std::move(callback).Run(verdict, std::move(problems));
+      },
+      std::move(routine), std::move(callback)));
 }
 
 void NetworkDiagnosticsImpl::HasSecureWiFiConnection(
     HasSecureWiFiConnectionCallback callback) {
-  HasSecureWiFiConnectionRoutine has_secure_wifi_connection_routine;
-  has_secure_wifi_connection_routine.RunRoutine(std::move(callback));
+  auto routine = std::make_unique<HasSecureWiFiConnectionRoutine>();
+  // RunRoutine() takes a lambda callback that takes ownership of the routine.
+  // This ensures that the routine stays alive when it makes asynchronous mojo
+  // calls. The routine will be destroyed when the lambda exits.
+  routine->RunRoutine(base::BindOnce(
+      [](std::unique_ptr<HasSecureWiFiConnectionRoutine> routine,
+         HasSecureWiFiConnectionCallback callback,
+         mojom::RoutineVerdict verdict,
+         const std::vector<mojom::HasSecureWiFiConnectionProblem>& problems) {
+        std::move(callback).Run(verdict, std::move(problems));
+      },
+      std::move(routine), std::move(callback)));
 }
 
 void NetworkDiagnosticsImpl::DnsResolverPresent(
     DnsResolverPresentCallback callback) {
-  DnsResolverPresentRoutine dns_resolver_present_routine;
-  dns_resolver_present_routine.RunRoutine(std::move(callback));
+  auto routine = std::make_unique<DnsResolverPresentRoutine>();
+  // RunRoutine() takes a lambda callback that takes ownership of the routine.
+  // This ensures that the routine stays alive when it makes asynchronous mojo
+  // calls. The routine will be destroyed when the lambda exits.
+  routine->RunRoutine(base::BindOnce(
+      [](std::unique_ptr<DnsResolverPresentRoutine> routine,
+         DnsResolverPresentCallback callback, mojom::RoutineVerdict verdict,
+         const std::vector<mojom::DnsResolverPresentProblem>& problems) {
+        std::move(callback).Run(verdict, std::move(problems));
+      },
+      std::move(routine), std::move(callback)));
 }
 
 void NetworkDiagnosticsImpl::DnsLatency(DnsLatencyCallback callback) {
-  DnsLatencyRoutine dns_latency_routine;
-  dns_latency_routine.RunRoutine(std::move(callback));
+  auto routine = std::make_unique<DnsLatencyRoutine>();
+  // RunRoutine() takes a lambda callback that takes ownership of the routine.
+  // This ensures that the routine stays alive when it makes asynchronous mojo
+  // calls. The routine will be destroyed when the lambda exits.
+  routine->RunRoutine(base::BindOnce(
+      [](std::unique_ptr<DnsLatencyRoutine> routine,
+         DnsLatencyCallback callback, mojom::RoutineVerdict verdict,
+         const std::vector<mojom::DnsLatencyProblem>& problems) {
+        std::move(callback).Run(verdict, std::move(problems));
+      },
+      std::move(routine), std::move(callback)));
 }
 
 void NetworkDiagnosticsImpl::DnsResolution(DnsResolutionCallback callback) {
-  DnsResolutionRoutine dns_resolution_routine;
-  dns_resolution_routine.RunRoutine(std::move(callback));
+  auto routine = std::make_unique<DnsResolutionRoutine>();
+  // RunRoutine() takes a lambda callback that takes ownership of the routine.
+  // This ensures that the routine stays alive when it makes asynchronous mojo
+  // calls. The routine will be destroyed when the lambda exits.
+  routine->RunRoutine(base::BindOnce(
+      [](std::unique_ptr<DnsResolutionRoutine> routine,
+         DnsResolutionCallback callback, mojom::RoutineVerdict verdict,
+         const std::vector<mojom::DnsResolutionProblem>& problems) {
+        std::move(callback).Run(verdict, std::move(problems));
+      },
+      std::move(routine), std::move(callback)));
 }
 
 }  // namespace network_diagnostics
diff --git a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl.h b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl.h
index 0c02153..cfac73842 100644
--- a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl.h
+++ b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl.h
@@ -11,11 +11,14 @@
 #include "mojo/public/cpp/bindings/receiver_set.h"
 
 namespace chromeos {
+class DebugDaemonClient;
+
 namespace network_diagnostics {
 
 class NetworkDiagnosticsImpl : public mojom::NetworkDiagnosticsRoutines {
  public:
-  NetworkDiagnosticsImpl();
+  explicit NetworkDiagnosticsImpl(
+      chromeos::DebugDaemonClient* debug_daemon_client);
   NetworkDiagnosticsImpl(const NetworkDiagnosticsImpl&) = delete;
   NetworkDiagnosticsImpl& operator=(const NetworkDiagnosticsImpl&) = delete;
   ~NetworkDiagnosticsImpl() override;
@@ -38,6 +41,8 @@
 
  private:
   mojo::ReceiverSet<mojom::NetworkDiagnosticsRoutines> receivers_;
+  // An unowned pointer to the DebugDaemonClient instance.
+  chromeos::DebugDaemonClient* debug_daemon_client_;
   base::WeakPtrFactory<NetworkDiagnosticsImpl> weak_factory_{this};
 };
 
diff --git a/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl_unittest.cc b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl_unittest.cc
new file mode 100644
index 0000000..8fe32d4
--- /dev/null
+++ b/chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl_unittest.cc
@@ -0,0 +1,386 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/net/network_diagnostics/network_diagnostics_impl.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/debug_daemon/fake_debug_daemon_client.h"
+#include "chromeos/login/login_state/login_state.h"
+#include "chromeos/network/managed_network_configuration_handler.h"
+#include "chromeos/network/network_cert_loader.h"
+#include "chromeos/network/network_configuration_handler.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_metadata_store.h"
+#include "chromeos/network/network_profile_handler.h"
+#include "chromeos/network/onc/onc_utils.h"
+#include "chromeos/network/proxy/ui_proxy_config_service.h"
+#include "chromeos/services/network_config/public/cpp/cros_network_config_test_helper.h"
+#include "chromeos/services/network_health/public/mojom/network_diagnostics.mojom.h"
+#include "components/onc/onc_constants.h"
+#include "components/onc/onc_pref_names.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
+#include "components/proxy_config/proxy_config_pref_names.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/browser_task_environment.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
+
+namespace chromeos {
+namespace network_diagnostics {
+
+namespace {
+
+// The IP v4 config path specified here must match the IP v4 config path
+// specified in NetworkStateTestHelper::ResetDevicesAndServices(), which itself
+// is based on the IP v4 config path used to set up IP v4 configs in
+// FakeShillManagerClient::SetupDefaultEnvironment().
+const char kIPv4ConfigPath[] = "ipconfig_v4_path";
+const std::vector<std::string> kWellFormedDnsServers = {
+    "192.168.1.100", "192.168.1.101", "192.168.1.102"};
+
+// This fakes a DebugDaemonClient by serving fake ICMP results when the
+// DebugDaemonClient calls TestICMP().
+class TestDebugDaemonClient : public chromeos::FakeDebugDaemonClient {
+ public:
+  TestDebugDaemonClient() = default;
+  TestDebugDaemonClient(const TestDebugDaemonClient&) = delete;
+  TestDebugDaemonClient& operator=(const TestDebugDaemonClient&) = delete;
+
+  ~TestDebugDaemonClient() override {}
+
+  void TestICMP(const std::string& ip_address,
+                TestICMPCallback callback) override {
+    // Invoke the test callback with fake output.
+    std::move(callback).Run(base::Optional<std::string>{icmp_output_});
+  }
+
+  void set_icmp_output(const std::string& icmp_output) {
+    icmp_output_ = icmp_output;
+  }
+
+ private:
+  std::string icmp_output_;
+};
+
+// Fake ICMP output. For more details, see:
+// https://gerrit.chromium.org/gerrit/#/c/30310/2/src/helpers/icmp.cc.
+const char kFakeValidICMPOutput[] = R"(
+    { "4.3.2.1":
+      { "sent": 4,
+        "recvd": 4,
+        "time": 3005,
+        "min": 5.789000,
+        "avg": 5.913000,
+        "max": 6.227000,
+        "dev": 0.197000 }
+    })";
+
+}  // namespace
+
+class NetworkDiagnosticsImplTest : public ::testing::Test {
+ public:
+  NetworkDiagnosticsImplTest() {
+    // Set TestDebugDaemonClient
+    test_debug_daemon_client_ = std::make_unique<TestDebugDaemonClient>();
+    network_diagnostics_impl_ = std::make_unique<NetworkDiagnosticsImpl>(
+        test_debug_daemon_client_.get());
+    network_diagnostics_impl_->BindReceiver(
+        network_diagnostics_.BindNewPipeAndPassReceiver());
+
+    // Initialize the ManagedNetworkConfigurationHandler and any associated
+    // properties.
+    LoginState::Initialize();
+    NetworkCertLoader::Initialize();
+    InitializeManagedNetworkConfigurationHandler();
+    // Note that |cros_network_config_test_helper_| must be initialized before
+    // any routine is initialized (routine initialization is done in
+    // NetworkDiagnosticsImpl). This is because |g_network_config_override| in
+    // OverrideInProcessInstanceForTesting() must be set up before the routines
+    // invoke BindToInProcessInstance(). See
+    // chromeos/services/network_config/in_process_instance.cc for further
+    // details.
+    cros_network_config_test_helper().Initialize(
+        managed_network_configuration_handler_.get());
+    // Wait until |cros_network_config_test_helper_| has initialized.
+    base::RunLoop().RunUntilIdle();
+
+    // Set up properties for the WiFi service.
+    SetUpWiFi();
+  }
+
+  ~NetworkDiagnosticsImplTest() override {
+    NetworkCertLoader::Shutdown();
+    LoginState::Shutdown();
+    managed_network_configuration_handler_.reset();
+    ui_proxy_config_service_.reset();
+    network_configuration_handler_.reset();
+    network_profile_handler_.reset();
+  }
+
+  void InitializeManagedNetworkConfigurationHandler() {
+    network_profile_handler_ = NetworkProfileHandler::InitializeForTesting();
+    network_configuration_handler_ =
+        base::WrapUnique<NetworkConfigurationHandler>(
+            NetworkConfigurationHandler::InitializeForTest(
+                network_state_helper().network_state_handler(),
+                cros_network_config_test_helper().network_device_handler()));
+
+    PrefProxyConfigTrackerImpl::RegisterProfilePrefs(user_prefs_.registry());
+    PrefProxyConfigTrackerImpl::RegisterPrefs(local_state_.registry());
+    ::onc::RegisterProfilePrefs(user_prefs_.registry());
+    ::onc::RegisterPrefs(local_state_.registry());
+
+    ui_proxy_config_service_ = std::make_unique<chromeos::UIProxyConfigService>(
+        &user_prefs_, &local_state_,
+        network_state_helper().network_state_handler(),
+        network_profile_handler_.get());
+
+    managed_network_configuration_handler_ =
+        ManagedNetworkConfigurationHandler::InitializeForTesting(
+            network_state_helper().network_state_handler(),
+            network_profile_handler_.get(),
+            cros_network_config_test_helper().network_device_handler(),
+            network_configuration_handler_.get(),
+            ui_proxy_config_service_.get());
+
+    managed_network_configuration_handler_->SetPolicy(
+        ::onc::ONC_SOURCE_DEVICE_POLICY,
+        /*userhash=*/std::string(),
+        /*network_configs_onc=*/base::ListValue(),
+        /*global_network_config=*/base::DictionaryValue());
+
+    // Wait until the |managed_network_configuration_handler_| is initialized
+    // and set up.
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SetUpWiFi() {
+    DCHECK(wifi_path_.empty());
+    // By default, NetworkStateTestHelper already adds a WiFi device, so, we
+    // do not need to add one here. All that remains to be done is configuring
+    // the WiFi service.
+    wifi_path_ = ConfigureService(
+        R"({"GUID": "wifi_guid", "Type": "wifi", "State": "online"})");
+    SetServiceProperty(wifi_path_, shill::kSignalStrengthProperty,
+                       base::Value(100));
+    SetServiceProperty(wifi_path_, shill::kSecurityClassProperty,
+                       base::Value(shill::kSecurityPsk));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Set up the name servers and change the IPConfigs for the WiFi device and
+  // service by overwriting the initial IPConfigs that are set up in
+  // FakeShillManagerClient::SetupDefaultEnvironment(). Attach name
+  // servers to the IP config.
+  void SetUpNameServers(const std::vector<std::string>& name_servers) {
+    DCHECK(!wifi_path_.empty());
+    // Set up the name servers
+    base::ListValue dns_servers;
+    for (const std::string& name_server : name_servers) {
+      dns_servers.AppendString(name_server);
+    }
+
+    // Set up the IP v4 config
+    base::DictionaryValue ip_config_v4_properties;
+    ip_config_v4_properties.SetKey(shill::kNameServersProperty,
+                                   base::Value(dns_servers.Clone()));
+    network_state_helper().ip_config_test()->AddIPConfig(
+        kIPv4ConfigPath, ip_config_v4_properties);
+    std::string wifi_device_path =
+        network_state_helper().device_test()->GetDevicePathForType(
+            shill::kTypeWifi);
+    network_state_helper().device_test()->SetDeviceProperty(
+        wifi_device_path, shill::kIPConfigsProperty, ip_config_v4_properties,
+        /*notify_changed=*/true);
+    SetServiceProperty(wifi_path_, shill::kIPConfigProperty,
+                       base::Value(kIPv4ConfigPath));
+
+    // Wait until the changed name servers have been notified (notification
+    // triggered by call to SetDeviceProperty() above) and that the |wifi_path_|
+    // has been set up.
+    base::RunLoop().RunUntilIdle();
+  }
+
+ protected:
+  std::string ConfigureService(const std::string& shill_json_string) {
+    return network_state_helper().ConfigureService(shill_json_string);
+  }
+
+  void SetServiceProperty(const std::string& service_path,
+                          const std::string& key,
+                          const base::Value& value) {
+    network_state_helper().SetServiceProperty(service_path, key, value);
+  }
+
+  network_config::CrosNetworkConfigTestHelper&
+  cros_network_config_test_helper() {
+    return cros_network_config_test_helper_;
+  }
+
+  chromeos::NetworkStateTestHelper& network_state_helper() {
+    return cros_network_config_test_helper_.network_state_helper();
+  }
+
+  base::WeakPtr<NetworkDiagnosticsImplTest> weak_ptr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
+  NetworkDiagnosticsImpl* network_diagnostics_impl() {
+    return network_diagnostics_impl_.get();
+  }
+
+  TestDebugDaemonClient* test_debug_daemon_client() {
+    return test_debug_daemon_client_.get();
+  }
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  std::string wifi_path_;
+  std::unique_ptr<TestDebugDaemonClient> test_debug_daemon_client_;
+  std::unique_ptr<NetworkProfileHandler> network_profile_handler_;
+  std::unique_ptr<NetworkConfigurationHandler> network_configuration_handler_;
+  std::unique_ptr<ManagedNetworkConfigurationHandler>
+      managed_network_configuration_handler_;
+  std::unique_ptr<UIProxyConfigService> ui_proxy_config_service_;
+  sync_preferences::TestingPrefServiceSyncable user_prefs_;
+  TestingPrefServiceSimple local_state_;
+
+  // |cros_network_config_test_helper_| must be initialized with the
+  // ManagedConfigurationHandler. This is done in
+  // InitializeManagedNetworkConfigurationHandler().
+  network_config::CrosNetworkConfigTestHelper cros_network_config_test_helper_{
+      false};
+  mojo::Remote<mojom::NetworkDiagnosticsRoutines> network_diagnostics_;
+  std::unique_ptr<NetworkDiagnosticsImpl> network_diagnostics_impl_;
+  base::WeakPtrFactory<NetworkDiagnosticsImplTest> weak_factory_{this};
+};
+
+// Test whether NetworkDiagnosticsImpl can successfully invoke the
+// LanConnectivity routine.
+TEST_F(NetworkDiagnosticsImplTest, LanConnectivityReachability) {
+  mojom::RoutineVerdict received_verdict;
+  base::RunLoop run_loop;
+  network_diagnostics_impl()->LanConnectivity(base::BindOnce(
+      [](mojom::RoutineVerdict* received_verdict,
+         base::OnceClosure quit_closure, mojom::RoutineVerdict actual_verdict) {
+        *received_verdict = actual_verdict;
+        std::move(quit_closure).Run();
+      },
+      &received_verdict, run_loop.QuitClosure()));
+  run_loop.Run();
+  EXPECT_EQ(received_verdict, mojom::RoutineVerdict::kNoProblem);
+}
+
+// Test whether NetworkDiagnosticsImpl can successfully invoke the
+// SignalStrength routine.
+TEST_F(NetworkDiagnosticsImplTest, SignalStrengthReachability) {
+  mojom::RoutineVerdict received_verdict;
+  std::vector<mojom::SignalStrengthProblem> received_problems;
+  base::RunLoop run_loop;
+  network_diagnostics_impl()->SignalStrength(base::BindOnce(
+      [](mojom::RoutineVerdict* received_verdict,
+         std::vector<mojom::SignalStrengthProblem>* received_problems,
+         base::OnceClosure quit_closure, mojom::RoutineVerdict actual_verdict,
+         const std::vector<mojom::SignalStrengthProblem>& actual_problems) {
+        *received_verdict = actual_verdict;
+        *received_problems = std::move(actual_problems);
+        std::move(quit_closure).Run();
+      },
+      &received_verdict, &received_problems, run_loop.QuitClosure()));
+  run_loop.Run();
+  EXPECT_EQ(received_verdict, mojom::RoutineVerdict::kNoProblem);
+  std::vector<mojom::SignalStrengthProblem> no_problems;
+  EXPECT_EQ(received_problems, no_problems);
+}
+
+// Test whether NetworkDiagnosticsImpl can successfully invoke the
+// GatewayCanBePinged routine.
+TEST_F(NetworkDiagnosticsImplTest, GatewayCanBePingedReachability) {
+  test_debug_daemon_client()->set_icmp_output(kFakeValidICMPOutput);
+  mojom::RoutineVerdict received_verdict;
+  std::vector<mojom::GatewayCanBePingedProblem> received_problems;
+  base::RunLoop run_loop;
+  network_diagnostics_impl()->GatewayCanBePinged(base::BindOnce(
+      [](mojom::RoutineVerdict* received_verdict,
+         std::vector<mojom::GatewayCanBePingedProblem>* received_problems,
+         base::OnceClosure quit_closure, mojom::RoutineVerdict actual_verdict,
+         const std::vector<mojom::GatewayCanBePingedProblem>& actual_problems) {
+        *received_verdict = actual_verdict;
+        *received_problems = std::move(actual_problems);
+        std::move(quit_closure).Run();
+      },
+      &received_verdict, &received_problems, run_loop.QuitClosure()));
+  run_loop.Run();
+  EXPECT_EQ(received_verdict, mojom::RoutineVerdict::kNoProblem);
+  std::vector<mojom::GatewayCanBePingedProblem> no_problems;
+  EXPECT_EQ(received_problems, no_problems);
+}
+
+// Test whether NetworkDiagnosticsImpl can successfully invoke the
+// HasSecureWiFiConnection routine.
+TEST_F(NetworkDiagnosticsImplTest, HasSecureWiFiConnectionReachability) {
+  mojom::RoutineVerdict received_verdict;
+  std::vector<mojom::HasSecureWiFiConnectionProblem> received_problems;
+  base::RunLoop run_loop;
+  network_diagnostics_impl()->HasSecureWiFiConnection(base::BindOnce(
+      [](mojom::RoutineVerdict* received_verdict,
+         std::vector<mojom::HasSecureWiFiConnectionProblem>* received_problems,
+         base::OnceClosure quit_closure, mojom::RoutineVerdict actual_verdict,
+         const std::vector<mojom::HasSecureWiFiConnectionProblem>&
+             actual_problems) {
+        *received_verdict = actual_verdict;
+        *received_problems = std::move(actual_problems);
+        std::move(quit_closure).Run();
+      },
+      &received_verdict, &received_problems, run_loop.QuitClosure()));
+  run_loop.Run();
+  EXPECT_EQ(received_verdict, mojom::RoutineVerdict::kNoProblem);
+  std::vector<mojom::HasSecureWiFiConnectionProblem> no_problems;
+  EXPECT_EQ(received_problems, no_problems);
+}
+
+// Test whether NetworkDiagnosticsImpl can successfully invoke the
+// DnsResolverPresent routine.
+TEST_F(NetworkDiagnosticsImplTest, DnsResolverPresentReachability) {
+  // Attach nameservers to the IPConfigs.
+  SetUpNameServers(kWellFormedDnsServers);
+
+  mojom::RoutineVerdict received_verdict;
+  std::vector<mojom::DnsResolverPresentProblem> received_problems;
+  base::RunLoop run_loop;
+  network_diagnostics_impl()->DnsResolverPresent(base::BindOnce(
+      [](mojom::RoutineVerdict* received_verdict,
+         std::vector<mojom::DnsResolverPresentProblem>* received_problems,
+         base::OnceClosure quit_closure, mojom::RoutineVerdict actual_verdict,
+         const std::vector<mojom::DnsResolverPresentProblem>& actual_problems) {
+        *received_verdict = actual_verdict;
+        *received_problems = std::move(actual_problems);
+        std::move(quit_closure).Run();
+      },
+      &received_verdict, &received_problems, run_loop.QuitClosure()));
+  run_loop.Run();
+  EXPECT_EQ(received_verdict, mojom::RoutineVerdict::kNoProblem);
+  std::vector<mojom::DnsResolverPresentProblem> no_problems;
+  EXPECT_EQ(received_problems, no_problems);
+}
+
+// TODO(khegde): Test whether NetworkDiagnosticsImpl can successfully invoke the
+// DnsLatency routine. This would require a way to fake and inject the following
+// into the DnsLatency routine: base::TickClock, network::mojom::HostResolver,
+// and network::TestNetworkContext.
+// TEST_F(NetworkDiagnosticsImplTest, DnsLatencyReachability) {}
+
+// TODO(khegde): Test whether NetworkDiagnosticsImpl can successfully invoke the
+// DnsResolution routine. This would require a way to fake and inject the
+// following into the DnsResolution routine: network::mojom::HostResolver and
+// network::TestNetworkContext.
+// TEST_F(NetworkDiagnosticsImplTest, DnsResolutionReachability) {}
+
+}  // namespace network_diagnostics
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/net/network_health/network_health_service.cc b/chrome/browser/chromeos/net/network_health/network_health_service.cc
index c68f55c..a54943f 100644
--- a/chrome/browser/chromeos/net/network_health/network_health_service.cc
+++ b/chrome/browser/chromeos/net/network_health/network_health_service.cc
@@ -5,11 +5,16 @@
 #include "chrome/browser/chromeos/net/network_health/network_health_service.h"
 
 #include "base/no_destructor.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
 
 namespace chromeos {
 namespace network_health {
 
-NetworkHealthService::NetworkHealthService() = default;
+NetworkHealthService::NetworkHealthService() {
+  network_diagnostics_ =
+      std::make_unique<network_diagnostics::NetworkDiagnosticsImpl>(
+          chromeos::DBusThreadManager::Get()->GetDebugDaemonClient());
+}
 
 void NetworkHealthService::BindRemote(
     mojo::PendingReceiver<mojom::NetworkHealthService> receiver) {
@@ -19,7 +24,7 @@
 void NetworkHealthService::BindDiagnosticsRemote(
     mojo::PendingReceiver<
         network_diagnostics::mojom::NetworkDiagnosticsRoutines> receiver) {
-  network_diagnostics_.BindReceiver(std::move(receiver));
+  network_diagnostics_->BindReceiver(std::move(receiver));
 }
 
 NetworkHealthService* NetworkHealthService::GetInstance() {
diff --git a/chrome/browser/chromeos/net/network_health/network_health_service.h b/chrome/browser/chromeos/net/network_health/network_health_service.h
index 12ee10b..8d378024 100644
--- a/chrome/browser/chromeos/net/network_health/network_health_service.h
+++ b/chrome/browser/chromeos/net/network_health/network_health_service.h
@@ -25,7 +25,8 @@
 
  private:
   NetworkHealth network_health_;
-  network_diagnostics::NetworkDiagnosticsImpl network_diagnostics_;
+  std::unique_ptr<network_diagnostics::NetworkDiagnosticsImpl>
+      network_diagnostics_;
 };
 
 }  // namespace network_health
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
index 55c094d..08829f6 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/chromeos/policy/wildcard_login_checker.h"
 #include "chrome/browser/enterprise/reporting/report_generator.h"
 #include "chrome/browser/enterprise/reporting/report_scheduler.h"
+#include "chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h"
 #include "chrome/browser/invalidation/profile_invalidation_provider_factory.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/net/system_network_context_manager.h"
@@ -777,8 +778,13 @@
     return;
   }
 
+  // TODO(crbug.com/1102047): Split up Chrome OS reporting code into its own
+  // delegates, then use the Chrome OS delegate factory here.
+  enterprise_reporting::ReportingDelegateFactoryDesktop delegate_factory;
   report_scheduler_ = std::make_unique<enterprise_reporting::ReportScheduler>(
-      client(), std::make_unique<enterprise_reporting::ReportGenerator>(),
+      client(),
+      std::make_unique<enterprise_reporting::ReportGenerator>(
+          &delegate_factory),
       profile_);
 
   report_scheduler_->OnDMTokenUpdated();
diff --git a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
index 651b914f..85ceb4c 100644
--- a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
@@ -332,7 +332,7 @@
   if (base::FeatureList::IsEnabled(download::features::kDownloadLater)) {
     pref_service_->SetInteger(
         prefs::kDownloadLaterPromptStatus,
-        static_cast<int>(DownloadLaterPromptStatus::DONT_SHOW));
+        static_cast<int>(DownloadLaterPromptStatus::kDontShow));
   }
 #endif
 }
diff --git a/chrome/browser/download/download_prefs.cc b/chrome/browser/download/download_prefs.cc
index 19f5cb8..6c30afb 100644
--- a/chrome/browser/download/download_prefs.cc
+++ b/chrome/browser/download/download_prefs.cc
@@ -198,8 +198,12 @@
   prompt_for_download_.Init(prefs::kPromptForDownload, prefs);
 #if defined(OS_ANDROID)
   prompt_for_download_android_.Init(prefs::kPromptForDownloadAndroid, prefs);
+  RecordDownloadPromptStatus(
+      static_cast<DownloadPromptStatus>(*prompt_for_download_android_));
   if (base::FeatureList::IsEnabled(download::features::kDownloadLater)) {
     prompt_for_download_later_.Init(prefs::kDownloadLaterPromptStatus, prefs);
+    RecordDownloadLaterPromptStatus(
+        static_cast<DownloadLaterPromptStatus>(*prompt_for_download_later_));
   }
 
   // If |kDownloadsLocationChange| is not enabled, always uses the default
@@ -311,14 +315,13 @@
   if (base::FeatureList::IsEnabled(download::features::kDownloadLater)) {
     registry->RegisterIntegerPref(
         prefs::kDownloadLaterPromptStatus,
-        static_cast<int>(DownloadLaterPromptStatus::SHOW_INITIAL),
+        static_cast<int>(DownloadLaterPromptStatus::kShowInitial),
         user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   }
 
   registry->RegisterBooleanPref(
       prefs::kShowMissingSdCardErrorAndroid,
       base::FeatureList::IsEnabled(features::kDownloadsLocationChange));
-  RecordDownloadPromptStatus(download_prompt_status);
 #endif
 }
 
@@ -402,7 +405,7 @@
 #ifdef OS_ANDROID
   if (base::FeatureList::IsEnabled(download::features::kDownloadLater)) {
     return *prompt_for_download_later_ !=
-           static_cast<int>(DownloadLaterPromptStatus::DONT_SHOW);
+           static_cast<int>(DownloadLaterPromptStatus::kDontShow);
   }
 #endif
 
diff --git a/chrome/browser/download/download_prefs_unittest.cc b/chrome/browser/download/download_prefs_unittest.cc
index e556311..03045bdd 100644
--- a/chrome/browser/download/download_prefs_unittest.cc
+++ b/chrome/browser/download/download_prefs_unittest.cc
@@ -55,6 +55,8 @@
   // Download prompt prefs should be registered correctly.
   histogram_tester.ExpectBucketCount("MobileDownload.DownloadPromptStatus",
                                      DownloadPromptStatus::SHOW_INITIAL, 1);
+  histogram_tester.ExpectBucketCount("MobileDownload.DownloadLaterPromptStatus",
+                                     DownloadPromptStatus::SHOW_INITIAL, 1);
   int prompt_status = profile.GetTestingPrefService()->GetInteger(
       prefs::kPromptForDownloadAndroid);
   EXPECT_EQ(prompt_status,
@@ -64,7 +66,7 @@
       profile.GetTestingPrefService()->GetInteger(
           prefs::kDownloadLaterPromptStatus);
   EXPECT_EQ(download_later_prompt_status,
-            static_cast<int>(DownloadLaterPromptStatus::SHOW_INITIAL));
+            static_cast<int>(DownloadLaterPromptStatus::kShowInitial));
 #endif  // OS_ANDROID
 }
 
diff --git a/chrome/browser/download/download_prompt_status.h b/chrome/browser/download/download_prompt_status.h
index 9c6fecb..c77f6fa 100644
--- a/chrome/browser/download/download_prompt_status.h
+++ b/chrome/browser/download/download_prompt_status.h
@@ -19,10 +19,10 @@
 // A Java counterpart will be generated for this enum.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.download
 enum class DownloadLaterPromptStatus {
-  SHOW_INITIAL,     // Show the prompt because it hasn't been shown before.
-  SHOW_PREFERENCE,  // Show the prompt because user indicated preference.
-  DONT_SHOW,        // Don't show the prompt because user indicated preference.
-  MAX_VALUE
+  kShowInitial,     // Show the prompt because it hasn't been shown before.
+  kShowPreference,  // Show the prompt because user indicated preference.
+  kDontShow,        // Don't show the prompt because user indicated preference.
+  kMaxValue = kDontShow
 };
 
 #endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_PROMPT_STATUS_H_
diff --git a/chrome/browser/download/download_stats.cc b/chrome/browser/download/download_stats.cc
index af19d8b9..7c27a70a0 100644
--- a/chrome/browser/download/download_stats.cc
+++ b/chrome/browser/download/download_stats.cc
@@ -75,4 +75,9 @@
   UMA_HISTOGRAM_ENUMERATION("MobileDownload.DownloadPromptStatus", status,
                             DownloadPromptStatus::MAX_VALUE);
 }
+
+void RecordDownloadLaterPromptStatus(DownloadLaterPromptStatus status) {
+  UMA_HISTOGRAM_ENUMERATION("MobileDownload.DownloadLaterPromptStatus", status);
+}
+
 #endif  // OS_ANDROID
diff --git a/chrome/browser/download/download_stats.h b/chrome/browser/download/download_stats.h
index 48d6b993e..3ecf1783 100644
--- a/chrome/browser/download/download_stats.h
+++ b/chrome/browser/download/download_stats.h
@@ -154,6 +154,9 @@
 #ifdef OS_ANDROID
 // Records whether the download dialog is shown to the user.
 void RecordDownloadPromptStatus(DownloadPromptStatus status);
+
+// Records whether the download later dialog is shown to the user.
+void RecordDownloadLaterPromptStatus(DownloadLaterPromptStatus status);
 #endif  // OS_ANDROID
 
 #endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_STATS_H_
diff --git a/chrome/browser/download/download_stats_unittest.cc b/chrome/browser/download/download_stats_unittest.cc
index 5d28350..a85f0e2d 100644
--- a/chrome/browser/download/download_stats_unittest.cc
+++ b/chrome/browser/download/download_stats_unittest.cc
@@ -16,6 +16,9 @@
 constexpr char kDownloadPromptStatusHistogram[] =
     "MobileDownload.DownloadPromptStatus";
 
+constexpr char kDownloadLaterPromptStatusHistogram[] =
+    "MobileDownload.DownloadLaterPromptStatus";
+
 TEST(DownloadStatsTest, RecordDownloadPromptStatus) {
   base::HistogramTester histogram_tester;
   RecordDownloadPromptStatus(DownloadPromptStatus::SHOW_INITIAL);
@@ -29,6 +32,15 @@
                                      DownloadPromptStatus::DONT_SHOW, 1);
   histogram_tester.ExpectTotalCount(kDownloadPromptStatusHistogram, 3);
 }
+
+TEST(DownloadStatsTest, RecordDownloadLaterPromptStatus) {
+  base::HistogramTester histogram_tester;
+  RecordDownloadLaterPromptStatus(DownloadLaterPromptStatus::kDontShow);
+  histogram_tester.ExpectBucketCount(kDownloadLaterPromptStatusHistogram,
+                                     DownloadLaterPromptStatus::kDontShow, 1);
+  histogram_tester.ExpectTotalCount(kDownloadLaterPromptStatusHistogram, 1);
+}
+
 #endif  // OS_ANDROID
 
 TEST(DownloadStatsTest, RecordDownloadCancelReason) {
diff --git a/chrome/browser/enterprise/reporting/report_generator.cc b/chrome/browser/enterprise/reporting/report_generator.cc
index 4edd2a38..65053b4 100644
--- a/chrome/browser/enterprise/reporting/report_generator.cc
+++ b/chrome/browser/enterprise/reporting/report_generator.cc
@@ -7,31 +7,23 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/logging.h"
-#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/arc/arc_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h"
 #include "components/policy/core/common/cloud/cloud_policy_util.h"
 
 #if defined(OS_WIN)
 #include "base/win/wmi.h"
 #endif
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/enterprise/reporting/android_app_info_generator.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
-#endif
-
 namespace em = enterprise_management;
 
 namespace enterprise_reporting {
 
-ReportGenerator::ReportGenerator()
-    : report_request_queue_generator_(&delegate_factory_),
-      browser_report_generator_(&delegate_factory_) {}
+ReportGenerator::ReportGenerator(
+    ReportingDelegateFactoryDesktop* delegate_factory)
+    : delegate_(delegate_factory->GetReportGeneratorDelegate()),
+      report_request_queue_generator_(delegate_factory),
+      browser_report_generator_(delegate_factory) {}
 
 ReportGenerator::~ReportGenerator() = default;
 
@@ -49,7 +41,7 @@
     bool with_profiles,
     ReportCallback callback) {
 #if defined(OS_CHROMEOS)
-  SetAndroidAppInfos(basic_request.get());
+  delegate_->SetAndroidAppInfos(basic_request.get());
 #else
   basic_request->set_computer_name(this->GetMachineName());
   basic_request->set_os_user_name(GetOSUserName());
@@ -87,38 +79,6 @@
 #endif
 }
 
-#if defined(OS_CHROMEOS)
-
-void ReportGenerator::SetAndroidAppInfos(ReportRequest* basic_request) {
-  DCHECK(basic_request);
-  basic_request->clear_android_app_infos();
-
-  // Android application is only supported for primary profile.
-  Profile* primary_profile =
-      g_browser_process->profile_manager()->GetPrimaryUserProfile();
-
-  if (!arc::IsArcPlayStoreEnabledForProfile(primary_profile))
-    return;
-
-  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(primary_profile);
-
-  if (!prefs) {
-    LOG(ERROR) << base::StringPrintf(
-        "Failed to generate ArcAppListPrefs instance for primary user profile "
-        "(debug name: %s).",
-        primary_profile->GetDebugName().c_str());
-    return;
-  }
-
-  AndroidAppInfoGenerator generator;
-  for (std::string app_id : prefs->GetAppIds()) {
-    basic_request->mutable_android_app_infos()->AddAllocated(
-        generator.Generate(prefs, app_id).release());
-  }
-}
-
-#endif
-
 void ReportGenerator::OnBrowserReportReady(
     bool with_profiles,
     ReportCallback callback,
diff --git a/chrome/browser/enterprise/reporting/report_generator.h b/chrome/browser/enterprise/reporting/report_generator.h
index 49e33fb6..75a6a0e9 100644
--- a/chrome/browser/enterprise/reporting/report_generator.h
+++ b/chrome/browser/enterprise/reporting/report_generator.h
@@ -9,10 +9,10 @@
 #include <queue>
 #include <string>
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
-#include "chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h"
 #include "components/enterprise/browser/reporting/browser_report_generator.h"
 #include "components/enterprise/browser/reporting/report_request_definition.h"
 #include "components/enterprise/browser/reporting/report_request_queue_generator.h"
@@ -20,13 +20,28 @@
 
 namespace enterprise_reporting {
 
+class ReportingDelegateFactoryDesktop;
+
 class ReportGenerator {
  public:
   using ReportRequest = definition::ReportRequest;
   using ReportRequests = std::queue<std::unique_ptr<ReportRequest>>;
   using ReportCallback = base::OnceCallback<void(ReportRequests)>;
 
-  ReportGenerator();
+  class Delegate {
+   public:
+    Delegate() = default;
+    Delegate(const Delegate&) = delete;
+    Delegate& operator=(const Delegate&) = delete;
+    virtual ~Delegate() = default;
+
+    // Collect the Android application information installed on primary profile,
+    // and set it to |basic_request_|. Only implemented for Chrome OS. The
+    // fields are empty on other platforms.
+    virtual void SetAndroidAppInfos(ReportRequest* basic_request) = 0;
+  };
+
+  explicit ReportGenerator(ReportingDelegateFactoryDesktop* delegate_factory);
   virtual ~ReportGenerator();
 
   // Asynchronously generates a queue of report requests, providing them to
@@ -57,12 +72,6 @@
   // on other platforms.
   virtual std::string GetSerialNumber();
 
-#if defined(OS_CHROMEOS)
-  // Collect the Android application information installed on primary profile,
-  // and set it to |basic_request_|.
-  virtual void SetAndroidAppInfos(ReportRequest* basic_request);
-#endif
-
  private:
   void OnBrowserReportReady(
       bool with_profiles,
@@ -70,10 +79,7 @@
       std::unique_ptr<ReportRequest> basic_request,
       std::unique_ptr<enterprise_management::BrowserReport> browser_report);
 
-  // TODO(crbug.com/1092442): Move the delegate factory ownership to
-  // ChromeBrowserCloudManagementController's delegate after CBCMController has
-  // been moved to components.
-  ReportingDelegateFactoryDesktop delegate_factory_;
+  std::unique_ptr<Delegate> delegate_;
 
   ReportRequestQueueGenerator report_request_queue_generator_;
   BrowserReportGenerator browser_report_generator_;
diff --git a/chrome/browser/enterprise/reporting/report_generator_desktop.cc b/chrome/browser/enterprise/reporting/report_generator_desktop.cc
new file mode 100644
index 0000000..45f28f556
--- /dev/null
+++ b/chrome/browser/enterprise/reporting/report_generator_desktop.cc
@@ -0,0 +1,58 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/enterprise/reporting/report_generator_desktop.h"
+
+#include <utility>
+
+#if defined(OS_CHROMEOS)
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/enterprise/reporting/android_app_info_generator.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
+#include "components/policy/core/common/cloud/cloud_policy_util.h"
+#endif  // defined(OS_CHROMEOS)
+
+namespace em = enterprise_management;
+
+namespace enterprise_reporting {
+
+// TODO(crbug.com/1102047): Split up Chrome OS reporting code into its own
+// delegates, then move this method's implementation to ReportGeneratorChromeOS.
+void ReportGeneratorDesktop::SetAndroidAppInfos(
+    ReportGenerator::ReportRequest* basic_request) {
+#if defined(OS_CHROMEOS)
+  DCHECK(basic_request);
+  basic_request->clear_android_app_infos();
+
+  // Android application is only supported for primary profile.
+  Profile* primary_profile =
+      g_browser_process->profile_manager()->GetPrimaryUserProfile();
+
+  if (!arc::IsArcPlayStoreEnabledForProfile(primary_profile))
+    return;
+
+  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(primary_profile);
+
+  if (!prefs) {
+    LOG(ERROR) << base::StringPrintf(
+        "Failed to generate ArcAppListPrefs instance for primary user profile "
+        "(debug name: %s).",
+        primary_profile->GetDebugName().c_str());
+    return;
+  }
+
+  AndroidAppInfoGenerator generator;
+  for (std::string app_id : prefs->GetAppIds()) {
+    basic_request->mutable_android_app_infos()->AddAllocated(
+        generator.Generate(prefs, app_id).release());
+  }
+#endif  // defined(OS_CHROMEOS)
+}
+
+}  // namespace enterprise_reporting
diff --git a/chrome/browser/enterprise/reporting/report_generator_desktop.h b/chrome/browser/enterprise/reporting/report_generator_desktop.h
new file mode 100644
index 0000000..eed1c8e
--- /dev/null
+++ b/chrome/browser/enterprise/reporting/report_generator_desktop.h
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ENTERPRISE_REPORTING_REPORT_GENERATOR_DESKTOP_H_
+#define CHROME_BROWSER_ENTERPRISE_REPORTING_REPORT_GENERATOR_DESKTOP_H_
+
+#include "chrome/browser/enterprise/reporting/report_generator.h"
+
+#include <memory>
+#include <string>
+
+namespace enterprise_reporting {
+
+/**
+ * Desktop implementation of the report generator delegate.
+ */
+class ReportGeneratorDesktop : public ReportGenerator::Delegate {
+ public:
+  ReportGeneratorDesktop() = default;
+  ReportGeneratorDesktop(const ReportGeneratorDesktop&) = delete;
+  ReportGeneratorDesktop& operator=(const ReportGeneratorDesktop&) = delete;
+  ~ReportGeneratorDesktop() override = default;
+
+  // ReportGenerator::Delegate implementation.
+  void SetAndroidAppInfos(
+      ReportGenerator::ReportRequest* basic_request) override;
+};
+
+}  // namespace enterprise_reporting
+
+#endif  // CHROME_BROWSER_ENTERPRISE_REPORTING_REPORT_GENERATOR_DESKTOP_H_
diff --git a/chrome/browser/enterprise/reporting/report_generator_unittest.cc b/chrome/browser/enterprise/reporting/report_generator_unittest.cc
index 9251a32..32ae217 100644
--- a/chrome/browser/enterprise/reporting/report_generator_unittest.cc
+++ b/chrome/browser/enterprise/reporting/report_generator_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
+#include "chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -137,7 +138,8 @@
   using ReportRequest = definition::ReportRequest;
 
   ReportGeneratorTest()
-      : profile_manager_(TestingBrowserProcess::GetGlobal()) {}
+      : generator_(&delegate_factory_),
+        profile_manager_(TestingBrowserProcess::GetGlobal()) {}
   ~ReportGeneratorTest() override = default;
 
   void SetUp() override {
@@ -277,6 +279,7 @@
         .AsUTF8Unsafe();
   }
 
+  ReportingDelegateFactoryDesktop delegate_factory_;
   ReportGenerator generator_;
 
   content::BrowserTaskEnvironment task_environment_;
diff --git a/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc b/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc
index a68077b2..f64cc5d 100644
--- a/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc
+++ b/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc
@@ -13,6 +13,8 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise/reporting/prefs.h"
+#include "chrome/browser/enterprise/reporting/report_generator.h"
+#include "chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/upgrade_detector/build_state.h"
 #include "chrome/common/chrome_constants.h"
@@ -63,6 +65,9 @@
 
 class MockReportGenerator : public ReportGenerator {
  public:
+  explicit MockReportGenerator(
+      ReportingDelegateFactoryDesktop* delegate_factory)
+      : ReportGenerator(delegate_factory) {}
   void Generate(bool with_profiles, ReportCallback callback) override {
     OnGenerate(with_profiles, callback);
   }
@@ -91,7 +96,8 @@
     ASSERT_TRUE(profile_manager_.SetUp());
     client_ptr_ = std::make_unique<policy::MockCloudPolicyClient>();
     client_ = client_ptr_.get();
-    generator_ptr_ = std::make_unique<MockReportGenerator>();
+    generator_ptr_ =
+        std::make_unique<MockReportGenerator>(&report_delegate_factory_);
     generator_ = generator_ptr_.get();
     uploader_ptr_ = std::make_unique<MockReportUploader>();
     uploader_ = uploader_ptr_.get();
@@ -180,6 +186,7 @@
   ScopedTestingLocalState local_state_;
   TestingProfileManager profile_manager_;
 
+  ReportingDelegateFactoryDesktop report_delegate_factory_;
   std::unique_ptr<ReportScheduler> scheduler_;
   policy::MockCloudPolicyClient* client_;
   MockReportGenerator* generator_;
diff --git a/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.cc b/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.cc
index 5fac239..60828b8 100644
--- a/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.cc
+++ b/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/enterprise/reporting/browser_report_generator_desktop.h"
 #include "chrome/browser/enterprise/reporting/profile_report_generator_desktop.h"
+#include "chrome/browser/enterprise/reporting/report_generator_desktop.h"
 
 namespace enterprise_reporting {
 
@@ -19,4 +20,9 @@
   return std::make_unique<ProfileReportGeneratorDesktop>();
 }
 
+std::unique_ptr<ReportGenerator::Delegate>
+ReportingDelegateFactoryDesktop::GetReportGeneratorDelegate() {
+  return std::make_unique<ReportGeneratorDesktop>();
+}
+
 }  // namespace enterprise_reporting
diff --git a/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h b/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h
index bf7d472..b864623 100644
--- a/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h
+++ b/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h
@@ -9,10 +9,11 @@
 
 #include <memory>
 
-namespace enterprise_reporting {
+#include "chrome/browser/enterprise/reporting/report_generator.h"
+#include "components/enterprise/browser/reporting/browser_report_generator.h"
+#include "components/enterprise/browser/reporting/profile_report_generator.h"
 
-class BrowserReportGenerator;
-class ProfileReportGenerator;
+namespace enterprise_reporting {
 
 // Desktop implementation of the reporting delegate factory. Creates desktop-
 // specific delegates for the enterprise reporting classes.
@@ -30,6 +31,8 @@
 
   std::unique_ptr<ProfileReportGenerator::Delegate>
   GetProfileReportGeneratorDelegate() override;
+
+  std::unique_ptr<ReportGenerator::Delegate> GetReportGeneratorDelegate();
 };
 
 }  // namespace enterprise_reporting
diff --git a/chrome/browser/extensions/api/page_capture/page_capture_api.cc b/chrome/browser/extensions/api/page_capture/page_capture_api.cc
index 31905b3..6d22581 100644
--- a/chrome/browser/extensions/api/page_capture/page_capture_api.cc
+++ b/chrome/browser/extensions/api/page_capture/page_capture_api.cc
@@ -31,6 +31,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/extensions/public_session_permission_helper.h"
+#include "extensions/common/permissions/api_permission_set.h"
 #endif
 
 using content::BrowserThread;
diff --git a/chrome/browser/extensions/api/page_capture/page_capture_api.h b/chrome/browser/extensions/api/page_capture/page_capture_api.h
index 9e81b48..62a9c41 100644
--- a/chrome/browser/extensions/api/page_capture/page_capture_api.h
+++ b/chrome/browser/extensions/api/page_capture/page_capture_api.h
@@ -24,6 +24,10 @@
 
 namespace extensions {
 
+#if defined(OS_CHROMEOS)
+class PermissionIDSet;
+#endif
+
 class PageCaptureSaveAsMHTMLFunction : public ExtensionFunction {
  public:
   PageCaptureSaveAsMHTMLFunction();
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 3f6b5318..f9af7ae 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -304,6 +304,8 @@
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[::prefs::kNearbySharingActiveProfilePrefName] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
+  (*s_whitelist)[::prefs::kNearbySharingDeviceNamePrefName] =
+      settings_api::PrefType::PREF_TYPE_STRING;
 
   // Search page.
   (*s_whitelist)[DefaultSearchManager::kDefaultSearchProviderDataPrefName] =
diff --git a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
index 2d5c8d6..1078559d 100644
--- a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
@@ -26,6 +26,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -959,14 +960,18 @@
 // extension modifying the same request header.
 TEST(ExtensionWebRequestHelpersTest,
      TestMergeOnBeforeSendHeadersResponses_DeclarativeNetRequest) {
+  using RequestHeaderType =
+      extension_web_request_api_helpers::RequestHeaderType;
+  base::HistogramTester histogram_tester;
+
   DNRRequestAction action_1 =
       CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS);
   action_1.request_headers_to_modify = {
       DNRRequestAction::HeaderInfo(
-          "key1", api::declarative_net_request::HEADER_OPERATION_SET,
+          "referer", api::declarative_net_request::HEADER_OPERATION_SET,
           "dnr_action_1"),
       DNRRequestAction::HeaderInfo(
-          "key2", api::declarative_net_request::HEADER_OPERATION_SET,
+          "cookie", api::declarative_net_request::HEADER_OPERATION_SET,
           "dnr_action_1"),
       DNRRequestAction::HeaderInfo(
           "key3", api::declarative_net_request::HEADER_OPERATION_REMOVE,
@@ -976,10 +981,10 @@
       CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS);
   action_2.request_headers_to_modify = {
       DNRRequestAction::HeaderInfo(
-          "key1", api::declarative_net_request::HEADER_OPERATION_REMOVE,
+          "referer", api::declarative_net_request::HEADER_OPERATION_REMOVE,
           base::nullopt),
       DNRRequestAction::HeaderInfo(
-          "key2", api::declarative_net_request::HEADER_OPERATION_SET,
+          "cookie", api::declarative_net_request::HEADER_OPERATION_SET,
           "dnr_action_2"),
       DNRRequestAction::HeaderInfo(
           "key3", api::declarative_net_request::HEADER_OPERATION_SET,
@@ -992,6 +997,7 @@
   info.dnr_actions->push_back(std::move(action_2));
 
   net::HttpRequestHeaders base_headers;
+  base_headers.SetHeader("referer", "original");
   base_headers.SetHeader("key3", "value 3");
   helpers::IgnoredActions ignored_actions;
   std::string header_value;
@@ -1006,17 +1012,28 @@
       info, deltas, &base_headers, &ignored_actions, &ignore1, &ignore2,
       &request_headers_modified, &matched_dnr_actions);
   // Header set by a prior action cannot be removed by a subsequent action.
-  ASSERT_TRUE(base_headers.GetHeader("key1", &header_value));
+  ASSERT_TRUE(base_headers.GetHeader("referer", &header_value));
   EXPECT_EQ("dnr_action_1", header_value);
 
   // Header set by a prior action cannot be set to a different value by a
   // subsequent action.
-  ASSERT_TRUE(base_headers.GetHeader("key2", &header_value));
+  ASSERT_TRUE(base_headers.GetHeader("cookie", &header_value));
   EXPECT_EQ("dnr_action_1", header_value);
 
   // Header removed by a prior action cannot be set by a subsequent action.
   EXPECT_FALSE(base_headers.HasHeader("key3"));
 
+  // Check that the appropriate values are recorded for histograms.
+  histogram_tester.ExpectUniqueSample(
+      "Extensions.DeclarativeNetRequest.RequestHeaderAdded",
+      RequestHeaderType::kCookie, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Extensions.DeclarativeNetRequest.RequestHeaderChanged",
+      RequestHeaderType::kReferer, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Extensions.DeclarativeNetRequest.RequestHeaderRemoved",
+      RequestHeaderType::kOther, 1);
+
   EXPECT_EQ(0u, ignored_actions.size());
   EXPECT_TRUE(request_headers_modified);
 }
@@ -1827,6 +1844,108 @@
   EXPECT_EQ(0u, ignored_actions.size());
 }
 
+// Test that the appropriate metrics are logged for declarative net request
+// actions which modify response headers.
+TEST(ExtensionWebRequestHelpersTest,
+     TestMergeOnHeadersReceivedResponses_DeclarativeNetRequestMetrics) {
+  using HeaderInfo = DNRRequestAction::HeaderInfo;
+  using ResponseHeaderType =
+      extension_web_request_api_helpers::ResponseHeaderType;
+  base::HistogramTester histogram_tester;
+  const ExtensionId ext_1 = "ext_1";
+
+  DNRRequestAction action_1 =
+      CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS);
+  action_1.extension_id = ext_1;
+
+  action_1.response_headers_to_modify = {
+      HeaderInfo("connection",
+                 api::declarative_net_request::HEADER_OPERATION_APPEND,
+                 "dnr_action_1"),
+      HeaderInfo("same_ext_key",
+                 api::declarative_net_request::HEADER_OPERATION_SET,
+                 "dnr_action_1"),
+      HeaderInfo("set-cookie",
+                 api::declarative_net_request::HEADER_OPERATION_REMOVE,
+                 base::nullopt),
+      HeaderInfo("warning",
+                 api::declarative_net_request::HEADER_OPERATION_REMOVE,
+                 base::nullopt)};
+
+  DNRRequestAction action_2 =
+      CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS);
+  action_2.extension_id = ext_1;
+  action_2.response_headers_to_modify = {
+      HeaderInfo("connection",
+                 api::declarative_net_request::HEADER_OPERATION_APPEND,
+                 "dnr_action_2"),
+      HeaderInfo("same_ext_key",
+                 api::declarative_net_request::HEADER_OPERATION_APPEND,
+                 "dnr_action_2")};
+
+  WebRequestInfoInitParams info_params;
+  info_params.url = GURL(kExampleUrl);
+  WebRequestInfo info(std::move(info_params));
+
+  info.dnr_actions = std::vector<DNRRequestAction>();
+  info.dnr_actions->push_back(std::move(action_1));
+  info.dnr_actions->push_back(std::move(action_2));
+
+  helpers::IgnoredActions ignored_actions;
+  std::string header_value;
+  EventResponseDeltas deltas;
+
+  char base_headers_string[] =
+      "HTTP/1.0 200 OK\r\n"
+      "set-cookie: Value1\r\n"
+      "\r\n";
+  auto base_headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+      net::HttpUtil::AssembleRawHeaders(base_headers_string));
+
+  bool response_headers_modified;
+  scoped_refptr<net::HttpResponseHeaders> new_headers;
+  GURL preserve_fragment_on_redirect_url;
+  std::vector<const DNRRequestAction*> matched_dnr_actions;
+
+  MergeOnHeadersReceivedResponses(
+      info, deltas, base_headers.get(), &new_headers,
+      &preserve_fragment_on_redirect_url, &ignored_actions,
+      &response_headers_modified, &matched_dnr_actions);
+  EXPECT_TRUE(new_headers.get());
+  EXPECT_TRUE(response_headers_modified);
+
+  size_t iter = 0;
+  std::string name;
+  std::string value;
+  std::multimap<std::string, std::string> actual_headers;
+  while (new_headers->EnumerateHeaderLines(&iter, &name, &value))
+    actual_headers.emplace(name, value);
+
+  std::multimap<std::string, std::string> expected_headers;
+  expected_headers.emplace("connection", "dnr_action_1");
+  expected_headers.emplace("connection", "dnr_action_2");
+  expected_headers.emplace("same_ext_key", "dnr_action_1");
+  expected_headers.emplace("same_ext_key", "dnr_action_2");
+  EXPECT_EQ(expected_headers, actual_headers);
+
+  EXPECT_TRUE(preserve_fragment_on_redirect_url.is_empty());
+  EXPECT_EQ(0u, ignored_actions.size());
+
+  // Multiple appends on the same header should generate only one entry.
+  histogram_tester.ExpectUniqueSample(
+      "Extensions.DeclarativeNetRequest.ResponseHeaderAdded",
+      ResponseHeaderType::kConnection, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Extensions.DeclarativeNetRequest.ResponseHeaderChanged",
+      ResponseHeaderType::kOther, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Extensions.DeclarativeNetRequest.ResponseHeaderRemoved",
+      ResponseHeaderType::kSetCookie, 1);
+
+  // There should be no entry for the "warning" header because it was not
+  // removed.
+}
+
 TEST(ExtensionWebRequestHelpersTest, TestMergeOnAuthRequiredResponses) {
   helpers::IgnoredActions ignored_actions;
   EventResponseDeltas deltas;
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
index 1499dbb..881d58e 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -57,6 +57,7 @@
 #include "extensions/browser/guest_view/extensions_guest_view_message_filter.h"
 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
 #include "extensions/browser/info_map.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/browser/url_loader_factory_manager.h"
 #include "extensions/browser/url_request_util.h"
 #include "extensions/browser/view_type_utils.h"
@@ -683,13 +684,6 @@
   ProcessMap::Get(context)->Insert(extension->id(),
                                    site_instance->GetProcess()->GetID(),
                                    site_instance->GetId());
-
-  content::GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(&InfoMap::RegisterExtensionProcess,
-                     ExtensionSystem::Get(context)->info_map(), extension->id(),
-                     site_instance->GetProcess()->GetID(),
-                     site_instance->GetId()));
 }
 
 void ChromeContentBrowserClientExtensionsPart::SiteInstanceDeleting(
@@ -708,13 +702,6 @@
   ProcessMap::Get(context)->Remove(extension->id(),
                                    site_instance->GetProcess()->GetID(),
                                    site_instance->GetId());
-
-  content::GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(&InfoMap::UnregisterExtensionProcess,
-                     ExtensionSystem::Get(context)->info_map(), extension->id(),
-                     site_instance->GetProcess()->GetID(),
-                     site_instance->GetId()));
 }
 
 void ChromeContentBrowserClientExtensionsPart::OverrideWebkitPrefs(
diff --git a/chrome/browser/extensions/chrome_info_map_unittest.cc b/chrome/browser/extensions/chrome_info_map_unittest.cc
index bb1f227..e2d1685a 100644
--- a/chrome/browser/extensions/chrome_info_map_unittest.cc
+++ b/chrome/browser/extensions/chrome_info_map_unittest.cc
@@ -97,16 +97,4 @@
   EXPECT_FALSE(match);
 }
 
-TEST_F(ChromeInfoMapTest, TestNotificationsDisabled) {
-  scoped_refptr<InfoMap> info_map(new InfoMap());
-  scoped_refptr<Extension> app(
-      LoadManifest("manifest_tests", "valid_app.json"));
-  info_map->AddExtension(app.get(), base::Time(), false, false);
-
-  EXPECT_FALSE(info_map->AreNotificationsDisabled(app->id()));
-  info_map->SetNotificationsDisabled(app->id(), true);
-  EXPECT_TRUE(info_map->AreNotificationsDisabled(app->id()));
-  info_map->SetNotificationsDisabled(app->id(), false);
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_action_runner_browsertest.cc b/chrome/browser/extensions/extension_action_runner_browsertest.cc
index 1613c8f7..8d4bd449 100644
--- a/chrome/browser/extensions/extension_action_runner_browsertest.cc
+++ b/chrome/browser/extensions/extension_action_runner_browsertest.cc
@@ -301,10 +301,10 @@
 IN_PROC_BROWSER_TEST_F(ExtensionActionRunnerBrowserTest,
                        RemoveExtensionWithPendingInjections) {
   // Load up two extensions, each with content scripts.
-  const Extension* extension1 =
+  scoped_refptr<const Extension> extension1 =
       CreateExtension(ALL_HOSTS, CONTENT_SCRIPT, WITHHOLD_PERMISSIONS);
   ASSERT_TRUE(extension1);
-  const Extension* extension2 =
+  scoped_refptr<const Extension> extension2 =
       CreateExtension(ALL_HOSTS, CONTENT_SCRIPT, WITHHOLD_PERMISSIONS);
   ASSERT_TRUE(extension2);
 
@@ -322,8 +322,8 @@
       browser(), embedded_test_server()->GetURL("/extensions/test_file.html"));
 
   // Both extensions should have pending requests.
-  EXPECT_TRUE(action_runner->WantsToRun(extension1));
-  EXPECT_TRUE(action_runner->WantsToRun(extension2));
+  EXPECT_TRUE(action_runner->WantsToRun(extension1.get()));
+  EXPECT_TRUE(action_runner->WantsToRun(extension2.get()));
 
   // Unload one of the extensions.
   UnloadExtension(extension2->id());
@@ -332,15 +332,15 @@
 
   // We should have pending requests for extension1, but not the removed
   // extension2.
-  EXPECT_TRUE(action_runner->WantsToRun(extension1));
-  EXPECT_FALSE(action_runner->WantsToRun(extension2));
+  EXPECT_TRUE(action_runner->WantsToRun(extension1.get()));
+  EXPECT_FALSE(action_runner->WantsToRun(extension2.get()));
 
   // We should still be able to run the request for extension1.
   ExtensionTestMessageListener inject_success_listener(
       new ExtensionTestMessageListener(kInjectSucceeded,
                                        false /* won't reply */));
   inject_success_listener.set_extension_id(extension1->id());
-  action_runner->RunAction(extension1, true);
+  action_runner->RunAction(extension1.get(), true);
   EXPECT_TRUE(inject_success_listener.WaitUntilSatisfied());
 }
 
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 8975ceb2..26af710 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -72,7 +72,6 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/crx_file/id_util.h"
 #include "components/favicon_base/favicon_url_parser.h"
-#include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_process_host.h"
@@ -87,6 +86,7 @@
 #include "extensions/browser/external_install_info.h"
 #include "extensions/browser/install_flag.h"
 #include "extensions/browser/management_policy.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/browser/runtime_data.h"
 #include "extensions/browser/uninstall_reason.h"
 #include "extensions/browser/unloaded_extension_reason.h"
@@ -1964,9 +1964,6 @@
       }
 
       process_map->RemoveAllFromProcess(process->GetID());
-      content::GetIOThreadTaskRunner({})->PostTask(
-          FROM_HERE, base::BindOnce(&InfoMap::UnregisterAllExtensionsInProcess,
-                                    system_->info_map(), process->GetID()));
       break;
     }
 
diff --git a/chrome/browser/extensions/extension_system_impl.cc b/chrome/browser/extensions/extension_system_impl.cc
index efa140d..8e433d5 100644
--- a/chrome/browser/extensions/extension_system_impl.cc
+++ b/chrome/browser/extensions/extension_system_impl.cc
@@ -234,9 +234,6 @@
       content_verifier_->Start();
     info_map()->SetContentVerifier(content_verifier_.get());
 #if defined(OS_CHROMEOS)
-    if (chromeos::ProfileHelper::IsLockScreenAppProfile(profile_))
-      info_map()->SetIsLockScreenContext(true);
-
     // This class is used to check the permissions of the force-installed
     // extensions inside the managed-guest session. It updates the local state
     // perf with the result, a boolean value deciding whether the full warning
diff --git a/chrome/browser/extensions/service_worker_apitest.cc b/chrome/browser/extensions/service_worker_apitest.cc
index a7f896b0..de849ce 100644
--- a/chrome/browser/extensions/service_worker_apitest.cc
+++ b/chrome/browser/extensions/service_worker_apitest.cc
@@ -62,6 +62,7 @@
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/process_manager.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/browser/service_worker/service_worker_test_utils.h"
 #include "extensions/browser/service_worker_task_queue.h"
 #include "extensions/common/api/test.h"
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index e13957d..c3b0dcf 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2943,6 +2943,16 @@
     "expiry_milestone": 85
   },
   {
+    "name": "mixed-forms-disable-autofill",
+    "owners": [ "carlosil" ],
+    "expiry_milestone": 88
+  },
+  {
+    "name": "mixed-forms-interstitial",
+    "owners": [ "carlosil" ],
+    "expiry_milestone": 88
+  },
+  {
     "name": "mobile-google-srp",
     "owners": [ "gambard" ],
     "expiry_milestone": 88
@@ -3238,6 +3248,11 @@
     "expiry_milestone": 95
   },
   {
+    "name": "omnibox-rich-autocompletion-min-char",
+    "owners": [ "manukh", "chrome-omnibox-team@google.com" ],
+    "expiry_milestone": 95
+  },
+  {
     "name": "omnibox-search-engine-logo",
     "owners": [ "wylieb", "chrome-omnibox-team@google.com" ],
     "expiry_milestone": 90
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 3f72c12..cb0dc7aa 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1325,6 +1325,19 @@
     "Allows tabs to be dragged between any browsers that support tabs, "
     "including apps";
 
+const char kMixedFormsDisableAutofillName[] =
+    "Disable autofill for mixed forms";
+const char kMixedFormsDisableAutofillDescription[] =
+    "If enabled, autofill is not allowed for mixed forms (forms on HTTPS sites "
+    "that submit over HTTP), and a warning bubble will be shown instead. "
+    "Autofill for passwords is not affected by this setting.";
+
+const char kMixedFormsInterstitialName[] = "Mixed forms interstitial";
+const char kMixedFormsInterstitialDescription[] =
+    "When enabled, a full-page interstitial warning is shown when a mixed "
+    "content form (a form on an HTTPS site that submits over HTTP) is "
+    "submitted.";
+
 const char kMobileIdentityConsistencyName[] = "Mobile identity consistency";
 const char kMobileIdentityConsistencyDescription[] =
     "Enables stronger identity consistency on mobile";
@@ -1488,6 +1501,10 @@
     "'2-Line UI' includes titles (and URLs when autocompleting titles) on a "
     "2nd line, 3) 'Title AC' autocompletes titles, and 4) 'Non-Prefix AC' "
     "autocompletes non-prefixes.";
+const char kOmniboxRichAutocompletionMinCharName[] =
+    "Omnibox Rich Autocompletion Min Characters";
+const char kOmniboxRichAutocompletionMinCharDescription[] =
+    "Specifies min input character length to trigger rich autocompletion.";
 
 const char kOmniboxOnFocusSuggestionsName[] = "Omnibox on-focus suggestions";
 const char kOmniboxOnFocusSuggestionsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index d287a982..a841ce7b 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -782,6 +782,12 @@
 extern const char kMixBrowserTypeTabsName[];
 extern const char kMixBrowserTypeTabsDescription[];
 
+extern const char kMixedFormsDisableAutofillName[];
+extern const char kMixedFormsDisableAutofillDescription[];
+
+extern const char kMixedFormsInterstitialName[];
+extern const char kMixedFormsInterstitialDescription[];
+
 extern const char kMobileIdentityConsistencyName[];
 extern const char kMobileIdentityConsistencyDescription[];
 
@@ -865,6 +871,8 @@
 
 extern const char kOmniboxRichAutocompletionName[];
 extern const char kOmniboxRichAutocompletionDescription[];
+extern const char kOmniboxRichAutocompletionMinCharName[];
+extern const char kOmniboxRichAutocompletionMinCharDescription[];
 
 extern const char kOmniboxOnFocusSuggestionsName[];
 extern const char kOmniboxOnFocusSuggestionsDescription[];
diff --git a/chrome/browser/history/android/android_provider_backend.cc b/chrome/browser/history/android/android_provider_backend.cc
index eb6bdf9b..f85d3c3 100644
--- a/chrome/browser/history/android/android_provider_backend.cc
+++ b/chrome/browser/history/android/android_provider_backend.cc
@@ -12,12 +12,12 @@
 #include "components/history/core/browser/android/favicon_sql_handler.h"
 #include "components/history/core/browser/android/urls_sql_handler.h"
 #include "components/history/core/browser/android/visit_sql_handler.h"
+#include "components/history/core/browser/favicon_database.h"
 #include "components/history/core/browser/history_backend.h"
 #include "components/history/core/browser/history_backend_client.h"
 #include "components/history/core/browser/history_backend_notifier.h"
 #include "components/history/core/browser/history_database.h"
 #include "components/history/core/browser/keyword_search_term.h"
-#include "components/history/core/browser/thumbnail_database.h"
 
 namespace history {
 
@@ -137,13 +137,13 @@
 
 AndroidProviderBackend::ScopedTransaction::ScopedTransaction(
     HistoryDatabase* history_db,
-    ThumbnailDatabase* thumbnail_db)
+    FaviconDatabase* favicon_db)
     : history_db_(history_db),
-      thumbnail_db_(thumbnail_db),
+      favicon_db_(favicon_db),
       committed_(false),
       history_transaction_nesting_(history_db_->transaction_nesting()),
-      thumbnail_transaction_nesting_(
-          thumbnail_db_ ? thumbnail_db_->transaction_nesting() : 0) {
+      favicon_transaction_nesting_(
+          favicon_db_ ? favicon_db_->transaction_nesting() : 0) {
   // Commit all existing transactions since the AndroidProviderBackend's
   // transaction is very like to be rolled back when compared with the others.
   // The existing transactions have been scheduled to commit by
@@ -155,40 +155,40 @@
     history_db_->CommitTransaction();
   history_db_->BeginTransaction();
 
-  if (thumbnail_db_) {
-    count = thumbnail_transaction_nesting_;
+  if (favicon_db_) {
+    count = favicon_transaction_nesting_;
     while (count--)
-      thumbnail_db_->CommitTransaction();
-    thumbnail_db_->BeginTransaction();
+      favicon_db_->CommitTransaction();
+    favicon_db_->BeginTransaction();
   }
 }
 
 AndroidProviderBackend::ScopedTransaction::~ScopedTransaction() {
   if (!committed_) {
     history_db_->RollbackTransaction();
-    if (thumbnail_db_)
-      thumbnail_db_->RollbackTransaction();
+    if (favicon_db_)
+      favicon_db_->RollbackTransaction();
   }
   // There is no transaction now.
   DCHECK_EQ(0, history_db_->transaction_nesting());
-  DCHECK(!thumbnail_db_ || 0 == thumbnail_db_->transaction_nesting());
+  DCHECK(!favicon_db_ || 0 == favicon_db_->transaction_nesting());
 
   int count = history_transaction_nesting_;
   while (count--)
     history_db_->BeginTransaction();
 
-  if (thumbnail_db_) {
-    count = thumbnail_transaction_nesting_;
+  if (favicon_db_) {
+    count = favicon_transaction_nesting_;
     while (count--)
-      thumbnail_db_->BeginTransaction();
+      favicon_db_->BeginTransaction();
   }
 }
 
 void AndroidProviderBackend::ScopedTransaction::Commit() {
   DCHECK(!committed_);
   history_db_->CommitTransaction();
-  if (thumbnail_db_)
-    thumbnail_db_->CommitTransaction();
+  if (favicon_db_)
+    favicon_db_->CommitTransaction();
   committed_ = true;
 }
 
@@ -198,13 +198,13 @@
 AndroidProviderBackend::AndroidProviderBackend(
     const base::FilePath& db_name,
     HistoryDatabase* history_db,
-    ThumbnailDatabase* thumbnail_db,
+    FaviconDatabase* favicon_db,
     HistoryBackendClient* backend_client,
     HistoryBackendNotifier* notifier)
     : android_cache_db_filename_(db_name),
       db_(&history_db->GetDB()),
       history_db_(history_db),
-      thumbnail_db_(thumbnail_db),
+      favicon_db_(favicon_db),
       backend_client_(backend_client),
       initialized_(false),
       notifier_(notifier) {
@@ -235,7 +235,7 @@
   if (projections.empty())
     return NULL;
 
-  ScopedTransaction transaction(history_db_, thumbnail_db_);
+  ScopedTransaction transaction(history_db_, favicon_db_);
 
   if (!EnsureInitializedAndUpdated())
     return NULL;
@@ -253,7 +253,7 @@
     int* updated_count) {
   HistoryNotifications notifications;
 
-  ScopedTransaction transaction(history_db_, thumbnail_db_);
+  ScopedTransaction transaction(history_db_, favicon_db_);
 
   if (!UpdateHistoryAndBookmarks(row, selection, selection_args, updated_count,
                                  &notifications))
@@ -268,7 +268,7 @@
     const HistoryAndBookmarkRow& values) {
   HistoryNotifications notifications;
 
-  ScopedTransaction transaction(history_db_, thumbnail_db_);
+  ScopedTransaction transaction(history_db_, favicon_db_);
 
   AndroidURLID id = InsertHistoryAndBookmark(values, true, &notifications);
   if (!id)
@@ -285,7 +285,7 @@
     int* deleted_count) {
   HistoryNotifications notifications;
 
-  ScopedTransaction transaction(history_db_, thumbnail_db_);
+  ScopedTransaction transaction(history_db_, favicon_db_);
 
   if (!DeleteHistoryAndBookmarks(selection, selection_args, deleted_count,
                                  &notifications))
@@ -302,7 +302,7 @@
     int* deleted_count) {
   HistoryNotifications notifications;
 
-  ScopedTransaction transaction(history_db_, thumbnail_db_);
+  ScopedTransaction transaction(history_db_, favicon_db_);
 
   if (!DeleteHistory(selection, selection_args, deleted_count, &notifications))
     return false;
@@ -373,7 +373,7 @@
         changed_urls.reset(new URLRows);
       changed_urls->push_back(url_row);
     }
-    if (thumbnail_db_ &&
+    if (favicon_db_ &&
         row.is_value_set_explicitly(HistoryAndBookmarkRow::FAVICON)) {
       if (!favicon)
         favicon.reset(new std::set<GURL>);
@@ -425,9 +425,9 @@
   changed_urls->push_back(url_row);
 
   std::unique_ptr<std::set<GURL>> favicon;
-  // No favicon should be changed if the thumbnail_db_ is not available.
+  // No favicon should be changed if the favicon_db_ is not available.
   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::FAVICON) &&
-      row.favicon_valid() && thumbnail_db_) {
+      row.favicon_valid() && favicon_db_) {
     favicon.reset(new std::set<GURL>);
     favicon->insert(url_row.url());
   }
@@ -701,8 +701,8 @@
   urls_handler_.reset(new UrlsSQLHandler(history_db_));
   visit_handler_.reset(new VisitSQLHandler(history_db_, history_db_));
   android_urls_handler_.reset(new AndroidURLsSQLHandler(history_db_));
-  if (thumbnail_db_)
-    favicon_handler_.reset(new FaviconSQLHandler(thumbnail_db_));
+  if (favicon_db_)
+    favicon_handler_.reset(new FaviconSQLHandler(favicon_db_));
   bookmark_model_handler_.reset(new BookmarkModelSQLHandler(history_db_));
   // The urls_handler must be pushed first, because the subsequent handlers
   // depend on its output.
@@ -837,14 +837,15 @@
 }
 
 bool AndroidProviderBackend::UpdateFavicon() {
-  ThumbnailDatabase::IconMappingEnumerator enumerator;
-
-  // We want the AndroidProviderBackend run without thumbnail_db_
-  if (!thumbnail_db_)
+  // This class works without a |favicon_db_|, so return true if one is not
+  // available (a return value of false mostly means this class won't do
+  // anything).
+  if (!favicon_db_)
     return true;
 
-  if (!thumbnail_db_->InitIconMappingEnumerator(
-          favicon_base::IconType::kFavicon, &enumerator))
+  FaviconDatabase::IconMappingEnumerator enumerator;
+  if (!favicon_db_->InitIconMappingEnumerator(favicon_base::IconType::kFavicon,
+                                              &enumerator))
     return false;
 
   IconMapping icon_mapping;
@@ -1023,8 +1024,8 @@
   favicon_base::FaviconID favicon_id = statement->statement()->ColumnInt64(4);
   if (favicon_id) {
     std::vector<FaviconBitmap> favicon_bitmaps;
-    if (!thumbnail_db_ ||
-        !thumbnail_db_->GetFaviconBitmaps(favicon_id, &favicon_bitmaps))
+    if (!favicon_db_ ||
+        !favicon_db_->GetFaviconBitmaps(favicon_id, &favicon_bitmaps))
       return false;
    scoped_refptr<base::RefCountedMemory> bitmap_data =
        favicon_bitmaps[0].bitmap_data;
@@ -1158,8 +1159,8 @@
     if (!deleted_rows)
       deleted_rows.reset(new URLRows);
     deleted_rows->push_back(url_row);
-    if (thumbnail_db_ &&
-        thumbnail_db_->GetIconMappingsForPageURL(url_row.url(), NULL)) {
+    if (favicon_db_ &&
+        favicon_db_->GetIconMappingsForPageURL(url_row.url(), NULL)) {
       if (!favicons)
         favicons.reset(new std::set<GURL>);
       favicons->insert(url_row.url());
diff --git a/chrome/browser/history/android/android_provider_backend.h b/chrome/browser/history/android/android_provider_backend.h
index 7823c98..4ddc94e 100644
--- a/chrome/browser/history/android/android_provider_backend.h
+++ b/chrome/browser/history/android/android_provider_backend.h
@@ -21,11 +21,11 @@
 namespace history {
 
 class AndroidProviderBackend;
+class FaviconDatabase;
 class HistoryBackend;
 class HistoryBackendClient;
 class HistoryBackendNotifier;
 class HistoryDatabase;
-class ThumbnailDatabase;
 
 // This class provides the query/insert/update/remove methods to implement
 // android.provider.Browser.BookmarkColumns and
@@ -45,7 +45,7 @@
  public:
   AndroidProviderBackend(const base::FilePath& cache_db_name,
                          HistoryDatabase* history_db,
-                         ThumbnailDatabase* thumbnail_db,
+                         FaviconDatabase* favicon_db,
                          HistoryBackendClient* backend_client,
                          HistoryBackendNotifier* notifier);
 
@@ -154,7 +154,7 @@
   // The scoped transaction for AndroidProviderBackend.
   //
   // The new transactions are started automatically in both history and
-  // thumbnail database and could be a nesting transaction, if so, rolling back
+  // favicon database and could be a nesting transaction, if so, rolling back
   // of this transaction will cause the exsting and subsequent nesting
   // transactions failed.
   //
@@ -166,8 +166,7 @@
   //
   class ScopedTransaction {
    public:
-    ScopedTransaction(HistoryDatabase* history_db,
-                      ThumbnailDatabase* thumbnail_db);
+    ScopedTransaction(HistoryDatabase* history_db, FaviconDatabase* favicon_db);
     ~ScopedTransaction();
 
     // Commit the transaction.
@@ -175,13 +174,13 @@
 
    private:
     HistoryDatabase* history_db_;
-    ThumbnailDatabase* thumbnail_db_;
+    FaviconDatabase* favicon_db_;
     // Whether the transaction was committed.
     bool committed_;
     // The count of the nested transaction in history database.
     const int history_transaction_nesting_;
-    // The count of the nested transaction in thumbnail database.
-    const int thumbnail_transaction_nesting_;
+    // The count of the nested transaction in favicon database.
+    const int favicon_transaction_nesting_;
 
     DISALLOW_COPY_AND_ASSIGN(ScopedTransaction);
   };
@@ -335,7 +334,7 @@
 
   HistoryDatabase* history_db_;
 
-  ThumbnailDatabase* thumbnail_db_;
+  FaviconDatabase* favicon_db_;
 
   HistoryBackendClient* backend_client_;
 
diff --git a/chrome/browser/history/android/android_provider_backend_unittest.cc b/chrome/browser/history/android/android_provider_backend_unittest.cc
index 9e0c71c..a8b3cc5 100644
--- a/chrome/browser/history/android/android_provider_backend_unittest.cc
+++ b/chrome/browser/history/android/android_provider_backend_unittest.cc
@@ -201,7 +201,7 @@
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
 
     history_db_name_ = temp_dir_.GetPath().AppendASCII(kHistoryFilename);
-    thumbnail_db_name_ = temp_dir_.GetPath().AppendASCII(kFaviconsFilename);
+    favicon_db_name_ = temp_dir_.GetPath().AppendASCII(kFaviconsFilename);
     android_cache_db_name_ =
         temp_dir_.GetPath().AppendASCII("TestAndroidCache.db");
   }
@@ -250,11 +250,11 @@
   AndroidProviderBackendNotifier notifier_;
   scoped_refptr<HistoryBackend> history_backend_;
   TestHistoryDatabase history_db_;
-  ThumbnailDatabase thumbnail_db_;
+  FaviconDatabase favicon_db_;
   base::ScopedTempDir temp_dir_;
   base::FilePath android_cache_db_name_;
   base::FilePath history_db_name_;
-  base::FilePath thumbnail_db_name_;
+  base::FilePath favicon_db_name_;
 
   TestingProfileManager profile_manager_;
   BookmarkModel* bookmark_model_;
@@ -322,17 +322,17 @@
   history_backend->Closing();
   }
 
-  // The history_db_name and thumbnail_db_name files should be created by
+  // The history_db_name and favicon_db_name files should be created by
   // HistoryBackend. We need to open the same database files.
   ASSERT_TRUE(base::PathExists(history_db_name_));
-  ASSERT_TRUE(base::PathExists(thumbnail_db_name_));
+  ASSERT_TRUE(base::PathExists(favicon_db_name_));
 
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   // Set url1 as bookmark.
   AddBookmark(url1);
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
 
   ASSERT_TRUE(backend->EnsureInitializedAndUpdated());
@@ -461,18 +461,18 @@
   history_backend->Closing();
   }
 
-  // The history_db_name and thumbnail_db_name files should be created by
+  // The history_db_name and favicon_db_name files should be created by
   // HistoryBackend. We need to open the same database files.
   ASSERT_TRUE(base::PathExists(history_db_name_));
-  ASSERT_TRUE(base::PathExists(thumbnail_db_name_));
+  ASSERT_TRUE(base::PathExists(favicon_db_name_));
 
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   // Set url1 as bookmark.
   AddBookmark(url1);
 
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
 
   std::vector<HistoryAndBookmarkRow::ColumnID> projections;
@@ -556,9 +556,9 @@
   row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
 
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
 
   ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
@@ -665,10 +665,10 @@
   row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
 
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
 
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
 
   ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
@@ -764,9 +764,9 @@
 
 TEST_F(AndroidProviderBackendTest, IsValidHistoryAndBookmarkRow) {
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
 
   // The created time and last visit time are too close to have required visit
@@ -854,9 +854,9 @@
   row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
 
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
 
   AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
@@ -1035,9 +1035,9 @@
   row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
 
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
 
   AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
@@ -1115,9 +1115,9 @@
   row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
 
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
 
   AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
@@ -1176,9 +1176,9 @@
   row1.set_title(UTF8ToUTF16("cnn"));
 
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
 
   AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
@@ -1206,12 +1206,12 @@
               notifier_.favicon_changed()->find(row1.url()));
 
   std::vector<IconMapping> icon_mappings;
-  EXPECT_TRUE(thumbnail_db_.GetIconMappingsForPageURL(
+  EXPECT_TRUE(favicon_db_.GetIconMappingsForPageURL(
       row1.url(), {favicon_base::IconType::kFavicon}, &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   std::vector<FaviconBitmap> favicon_bitmaps;
-  EXPECT_TRUE(thumbnail_db_.GetFaviconBitmaps(icon_mappings[0].icon_id,
-                                              &favicon_bitmaps));
+  EXPECT_TRUE(favicon_db_.GetFaviconBitmaps(icon_mappings[0].icon_id,
+                                            &favicon_bitmaps));
   EXPECT_EQ(1u, favicon_bitmaps.size());
   EXPECT_TRUE(favicon_bitmaps[0].bitmap_data.get());
   EXPECT_EQ(1u, favicon_bitmaps[0].bitmap_data->size());
@@ -1235,15 +1235,15 @@
   ASSERT_TRUE(notifier_.favicon_changed()->end() !=
               notifier_.favicon_changed()->find(row1.url()));
 
-  EXPECT_FALSE(thumbnail_db_.GetIconMappingsForPageURL(
+  EXPECT_FALSE(favicon_db_.GetIconMappingsForPageURL(
       row1.url(), {favicon_base::IconType::kFavicon}, NULL));
 }
 
 TEST_F(AndroidProviderBackendTest, UpdateSearchTermTable) {
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
   // Insert a keyword search item to verify if the update succeeds.
   HistoryAndBookmarkRow row1;
@@ -1319,9 +1319,9 @@
 
 TEST_F(AndroidProviderBackendTest, QuerySearchTerms) {
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
   // Insert a keyword search item to verify if we can find it.
   HistoryAndBookmarkRow row1;
@@ -1353,9 +1353,9 @@
 
 TEST_F(AndroidProviderBackendTest, UpdateSearchTerms) {
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
   // Insert a keyword.
   HistoryAndBookmarkRow row1;
@@ -1457,9 +1457,9 @@
 
 TEST_F(AndroidProviderBackendTest, DeleteSearchTerms) {
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
   // Insert a keyword.
   HistoryAndBookmarkRow row1;
@@ -1563,9 +1563,9 @@
 
 TEST_F(AndroidProviderBackendTest, InsertSearchTerm) {
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
   SearchRow search_row;
   search_row.set_search_term(UTF8ToUTF16("google"));
@@ -1617,9 +1617,9 @@
   row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
 
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
 
   AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
@@ -1672,19 +1672,19 @@
 
 TEST_F(AndroidProviderBackendTest, TestMultipleNestingTransaction) {
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
 
   // Create the nested transactions.
   history_db_.BeginTransaction();
   history_db_.BeginTransaction();
   history_db_.BeginTransaction();
-  thumbnail_db_.BeginTransaction();
-  thumbnail_db_.BeginTransaction();
+  favicon_db_.BeginTransaction();
+  favicon_db_.BeginTransaction();
   int history_transaction = history_db_.transaction_nesting();
-  int thumbnail_transaction = thumbnail_db_.transaction_nesting();
+  int favicon_transaction = favicon_db_.transaction_nesting();
 
   // Insert a row to verify the transaction number are not changed
   // after a transaction commit.
@@ -1697,13 +1697,13 @@
   row1.set_title(UTF8ToUTF16("cnn"));
   ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
   EXPECT_EQ(history_transaction, history_db_.transaction_nesting());
-  EXPECT_EQ(thumbnail_transaction, thumbnail_db_.transaction_nesting());
+  EXPECT_EQ(favicon_transaction, favicon_db_.transaction_nesting());
 
   // Insert the same URL, it should failed. The transaction are still same
   // after a rollback.
   ASSERT_FALSE(backend->InsertHistoryAndBookmark(row1));
   EXPECT_EQ(history_transaction, history_db_.transaction_nesting());
-  EXPECT_EQ(thumbnail_transaction, thumbnail_db_.transaction_nesting());
+  EXPECT_EQ(favicon_transaction, favicon_db_.transaction_nesting());
 
   // Insert another row to verify we are still fine after the previous
   // rollback.
@@ -1715,16 +1715,16 @@
   row2.set_title(UTF8ToUTF16("example"));
   ASSERT_TRUE(backend->InsertHistoryAndBookmark(row2));
   EXPECT_EQ(history_transaction, history_db_.transaction_nesting());
-  EXPECT_EQ(thumbnail_transaction, thumbnail_db_.transaction_nesting());
+  EXPECT_EQ(favicon_transaction, favicon_db_.transaction_nesting());
 }
 
 TEST_F(AndroidProviderBackendTest, TestAndroidCTSComplianceForZeroVisitCount) {
   // This is to verify the last visit time and created time are same when visit
   // count is 0.
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
   URLRow url_row(GURL("http://www.google.com"));
   url_row.set_last_visit(Time::Now());
@@ -1759,9 +1759,9 @@
   // returned when folder is 0 and the non bookmark rows returned when folder
   // is 1.
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
-  ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
+  ASSERT_EQ(sql::INIT_OK, favicon_db_.Init(favicon_db_name_));
   std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-      android_cache_db_name_, &history_db_, &thumbnail_db_,
+      android_cache_db_name_, &history_db_, &favicon_db_,
       history_backend_client_.get(), &notifier_));
   HistoryAndBookmarkRow row1;
   row1.set_raw_url("cnn.com");
@@ -1809,7 +1809,7 @@
   EXPECT_FALSE(statement->statement()->Step());
 }
 
-TEST_F(AndroidProviderBackendTest, QueryWithoutThumbnailDB) {
+TEST_F(AndroidProviderBackendTest, QueryWithoutFaviconDB) {
   GURL url1("http://www.cnn.com");
   const base::string16 title1(UTF8ToUTF16("cnn"));
   std::vector<VisitInfo> visits1;
@@ -1858,10 +1858,10 @@
   history_backend->Closing();
   }
 
-  // The history_db_name and thumbnail_db_name files should be created by
+  // The history_db_name and favicon_db_name files should be created by
   // HistoryBackend. We need to open the same database files.
   ASSERT_TRUE(base::PathExists(history_db_name_));
-  ASSERT_TRUE(base::PathExists(thumbnail_db_name_));
+  ASSERT_TRUE(base::PathExists(favicon_db_name_));
 
   // Only creates the history database
   ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
@@ -1910,7 +1910,7 @@
   EXPECT_EQ(3, statement->statement()->ColumnInt(5));
   std::vector<unsigned char> favicon2;
   EXPECT_EQ(6, statement->favicon_index());
-  // No favicon because thumbnail database wasn't initialized.
+  // No favicon because favicon database wasn't initialized.
   EXPECT_EQ(0, statement->statement()->ColumnByteLength(6));
   EXPECT_FALSE(statement->statement()->ColumnBool(7));
 
@@ -1918,7 +1918,7 @@
   EXPECT_FALSE(statement->statement()->Step());
 }
 
-TEST_F(AndroidProviderBackendTest, InsertWithoutThumbnailDB) {
+TEST_F(AndroidProviderBackendTest, InsertWithoutFaviconDB) {
   HistoryAndBookmarkRow row1;
   row1.set_raw_url("cnn.com");
   row1.set_url(GURL("http://cnn.com"));
@@ -1973,12 +1973,12 @@
             (*notifier_.modified_details())[0].last_visit());
   EXPECT_EQ(row2.title(),
             (*notifier_.modified_details())[0].title());
-  // Favicon details is still false because thumbnail database wasn't
+  // Favicon details is still false because favicon database wasn't
   // initialized, we ignore any changes of favicon.
   ASSERT_FALSE(notifier_.favicon_changed());
 }
 
-TEST_F(AndroidProviderBackendTest, DeleteWithoutThumbnailDB) {
+TEST_F(AndroidProviderBackendTest, DeleteWithoutFaviconDB) {
   HistoryAndBookmarkRow row1;
   row1.set_raw_url("cnn.com");
   row1.set_url(GURL("http://cnn.com"));
@@ -2000,12 +2000,12 @@
 
   {
     TestHistoryDatabase history_db;
-    ThumbnailDatabase thumbnail_db;
+    FaviconDatabase favicon_db;
     ASSERT_EQ(sql::INIT_OK, history_db.Init(history_db_name_));
-    ASSERT_EQ(sql::INIT_OK, thumbnail_db.Init(thumbnail_db_name_));
+    ASSERT_EQ(sql::INIT_OK, favicon_db.Init(favicon_db_name_));
 
     std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-        android_cache_db_name_, &history_db, &thumbnail_db,
+        android_cache_db_name_, &history_db, &favicon_db,
         history_backend_client_.get(), &notifier_));
 
     ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
@@ -2061,7 +2061,7 @@
   ASSERT_FALSE(statement1->statement()->Step());
 }
 
-TEST_F(AndroidProviderBackendTest, UpdateFaviconWithoutThumbnail) {
+TEST_F(AndroidProviderBackendTest, UpdateFaviconWithoutFavicon) {
   HistoryAndBookmarkRow row1;
   row1.set_raw_url("cnn.com");
   row1.set_url(GURL("http://cnn.com"));
@@ -2073,11 +2073,11 @@
 
   {
     TestHistoryDatabase history_db;
-    ThumbnailDatabase thumbnail_db;
+    FaviconDatabase favicon_db;
     ASSERT_EQ(sql::INIT_OK, history_db.Init(history_db_name_));
-    ASSERT_EQ(sql::INIT_OK, thumbnail_db.Init(thumbnail_db_name_));
+    ASSERT_EQ(sql::INIT_OK, favicon_db.Init(favicon_db_name_));
     std::unique_ptr<AndroidProviderBackend> backend(new AndroidProviderBackend(
-        android_cache_db_name_, &history_db, &thumbnail_db,
+        android_cache_db_name_, &history_db, &favicon_db,
         history_backend_client_.get(), &notifier_));
 
     AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
@@ -2108,7 +2108,7 @@
   EXPECT_FALSE(notifier_.deleted_details());
   ASSERT_TRUE(notifier_.modified_details());
   ASSERT_EQ(1u, notifier_.modified_details()->size());
-  // No favicon will be updated as thumbnail database is missing.
+  // No favicon will be updated as favicon database is missing.
   EXPECT_FALSE(notifier_.favicon_changed());
 }
 
diff --git a/chrome/browser/history/chrome_history_backend_client.cc b/chrome/browser/history/chrome_history_backend_client.cc
index 49e6d1b4..a68ce538 100644
--- a/chrome/browser/history/chrome_history_backend_client.cc
+++ b/chrome/browser/history/chrome_history_backend_client.cc
@@ -74,15 +74,15 @@
 void ChromeHistoryBackendClient::OnHistoryBackendInitialized(
     history::HistoryBackend* history_backend,
     history::HistoryDatabase* history_database,
-    history::ThumbnailDatabase* thumbnail_database,
+    history::FaviconDatabase* favicon_database,
     const base::FilePath& history_dir) {
   DCHECK(history_backend);
-  if (thumbnail_database) {
+  if (favicon_database) {
     history_backend->SetUserData(
         history::AndroidProviderBackend::GetUserDataKey(),
         std::make_unique<history::AndroidProviderBackend>(
             history_dir.Append(kAndroidCacheFilename), history_database,
-            thumbnail_database, this, history_backend));
+            favicon_database, this, history_backend));
   }
 }
 
diff --git a/chrome/browser/history/chrome_history_backend_client.h b/chrome/browser/history/chrome_history_backend_client.h
index 8a51edb..03cbe5a8 100644
--- a/chrome/browser/history/chrome_history_backend_client.h
+++ b/chrome/browser/history/chrome_history_backend_client.h
@@ -27,11 +27,10 @@
   std::vector<history::URLAndTitle> GetPinnedURLs() override;
   bool IsWebSafe(const GURL& url) override;
 #if defined(OS_ANDROID)
-  void OnHistoryBackendInitialized(
-      history::HistoryBackend* history_backend,
-      history::HistoryDatabase* history_database,
-      history::ThumbnailDatabase* thumbnail_database,
-      const base::FilePath& history_dir) override;
+  void OnHistoryBackendInitialized(history::HistoryBackend* history_backend,
+                                   history::HistoryDatabase* history_database,
+                                   history::FaviconDatabase* favicon_database,
+                                   const base::FilePath& history_dir) override;
   void OnHistoryBackendDestroyed(history::HistoryBackend* history_backend,
                                  const base::FilePath& history_dir) override;
 #endif  // defined(OS_ANDROID)
diff --git a/chrome/browser/media/unified_autoplay_browsertest.cc b/chrome/browser/media/unified_autoplay_browsertest.cc
index d77c9e8..e9a093c 100644
--- a/chrome/browser/media/unified_autoplay_browsertest.cc
+++ b/chrome/browser/media/unified_autoplay_browsertest.cc
@@ -520,7 +520,8 @@
   EXPECT_TRUE(AutoplayAllowed(main_frame()));
 }
 
-IN_PROC_BROWSER_TEST_F(UnifiedAutoplaySettingBrowserTest, Block) {
+// Flaky. See https://crbug.com/1106521.
+IN_PROC_BROWSER_TEST_F(UnifiedAutoplaySettingBrowserTest, DISABLED_Block) {
   GURL main_url(
       embedded_test_server()->GetURL("example.com", kFramedTestPagePath));
   GURL foo_url(embedded_test_server()->GetURL("foo.com", kFramedTestPagePath));
@@ -562,7 +563,9 @@
   EXPECT_FALSE(AutoplayAllowed(main_frame()));
 }
 
-IN_PROC_BROWSER_TEST_F(UnifiedAutoplaySettingBrowserTest, DefaultAllow) {
+// Flaky. See https://crbug.com/1101524.
+IN_PROC_BROWSER_TEST_F(UnifiedAutoplaySettingBrowserTest,
+                       DISABLED_DefaultAllow) {
   GURL main_url(
       embedded_test_server()->GetURL("example.com", kFramedTestPagePath));
   GURL foo_url(embedded_test_server()->GetURL("foo.com", kFramedTestPagePath));
diff --git a/chrome/browser/notifications/notifier_state_tracker.cc b/chrome/browser/notifications/notifier_state_tracker.cc
index d953455..5a43225f 100644
--- a/chrome/browser/notifications/notifier_state_tracker.cc
+++ b/chrome/browser/notifications/notifier_state_tracker.cc
@@ -19,18 +19,14 @@
 #include "components/permissions/permission_result.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "content/public/browser/browser_task_traits.h"
 #include "extensions/buildflags/buildflags.h"
 #include "ui/message_center/public/cpp/notifier_id.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/api/notifications.h"
-#include "content/public/browser/browser_thread.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_event_histogram_value.h"
-#include "extensions/browser/extension_system.h"
 #include "extensions/browser/extension_util.h"
-#include "extensions/browser/info_map.h"
 #endif
 
 using message_center::NotifierId;
@@ -187,13 +183,5 @@
       std::move(args)));
 
   event_router->DispatchEventToExtension(notifier_id.id, std::move(event));
-
-  // Tell the IO thread that this extension's permission for notifications
-  // has changed.
-  extensions::InfoMap* extension_info_map =
-      extensions::ExtensionSystem::Get(profile_)->info_map();
-  content::GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(&extensions::InfoMap::SetNotificationsDisabled,
-                                extension_info_map, notifier_id.id, !enabled));
 }
 #endif
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
index c6b695a..b19c1ccf2 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
@@ -275,11 +275,13 @@
   ancestor_data->SetFirstEligibleToPaint(
       timing.paint_timing->first_eligible_to_paint);
 
-  // Set creative origin status if this is the first FCP for any frame in the
-  // root ad frame's subtree.
-  if (timing.paint_timing->first_contentful_paint &&
-      ancestor_data->creative_origin_status() ==
-          FrameData::OriginStatus::kUnknown) {
+  // Update earliest FCP as needed.
+  bool has_new_fcp = ancestor_data->SetEarliestFirstContentfulPaint(
+      timing.paint_timing->first_contentful_paint);
+
+  // If this is the earliest FCP for any frame in the root ad frame's subtree,
+  // set Creative Origin Status.
+  if (has_new_fcp) {
     FrameData::OriginStatus origin_status =
         AdsPageLoadMetricsObserver::IsSubframeSameOriginToMainFrame(
             subframe_rfh,
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index ed49e52..880054e 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -33,6 +33,7 @@
 #include "components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h"
 #include "components/page_load_metrics/browser/page_load_metrics_observer.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
+#include "components/page_load_metrics/common/page_load_metrics_util.h"
 #include "components/page_load_metrics/common/test/page_load_metrics_test_util.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
 #include "components/subresource_filter/core/common/load_policy.h"
@@ -64,6 +65,7 @@
 using content::RenderFrameHost;
 using content::RenderFrameHostTester;
 using content::TestNavigationThrottle;
+using page_load_metrics::OptionalMin;
 
 namespace {
 
@@ -114,6 +116,36 @@
     AdsPageLoadMetricsObserver::HeavyAdThresholdNoiseProvider::
         kMaxNetworkThresholdNoiseBytes;
 
+// Calls PopulateRequiredTimingFields with |first_eligible_to_paint| and
+// |first_contentful_paint| fields temporarily nullified.
+void PopulateRequiredTimingFieldsExceptFEtPAndFCP(
+    page_load_metrics::mojom::PageLoadTiming* inout_timing) {
+  // Save FEtP and FCP values in temp variables and then reset the fields.
+  auto first_eligible_to_paint =
+      inout_timing->paint_timing->first_eligible_to_paint;
+  inout_timing->paint_timing->first_eligible_to_paint.reset();
+
+  auto first_contentful_paint =
+      inout_timing->paint_timing->first_contentful_paint;
+  inout_timing->paint_timing->first_contentful_paint.reset();
+
+  // Populate required fields that don't depend on FEtP or FCP.
+  PopulateRequiredTimingFields(inout_timing);
+
+  // Reinstate REtP and FCP values.
+  inout_timing->paint_timing->first_eligible_to_paint = first_eligible_to_paint;
+  inout_timing->paint_timing->first_contentful_paint = first_contentful_paint;
+
+  // Populate |first_paint| field if needed.
+  if ((inout_timing->paint_timing->first_image_paint ||
+       inout_timing->paint_timing->first_contentful_paint) &&
+      !inout_timing->paint_timing->first_paint) {
+    inout_timing->paint_timing->first_paint =
+        OptionalMin(inout_timing->paint_timing->first_image_paint,
+                    inout_timing->paint_timing->first_contentful_paint);
+  }
+}
+
 // Asynchronously cancels the navigation at WillProcessResponse. Before
 // cancelling, simulates loading a main frame resource.
 class ResourceLoadingCancellingThrottle
@@ -555,7 +587,7 @@
     if (first_contentful_paint.has_value())
       timing_.paint_timing->first_contentful_paint =
           first_contentful_paint.value();
-    PopulateRequiredTimingFields(&timing_);
+    PopulateRequiredTimingFieldsExceptFEtPAndFCP(&timing_);
     tester()->SimulateTimingUpdate(timing_, frame);
   }
 
@@ -2032,7 +2064,55 @@
        {false, false} /* throttled */,
        0 /* creative_index */,
        false /* should_paint */,
-       OriginStatusWithThrottling::kUnknownAndUnthrottled}};
+       OriginStatusWithThrottling::kUnknownAndUnthrottled},
+      {"http://a.com",
+       {"http://a.com/disallowed.html", "http://b.com"},
+       {false, true} /* throttled */,
+       0 /* creative_index */,
+       false /* should_paint */,
+       OriginStatusWithThrottling::kUnknownAndThrottled},
+      {"http://a.com",
+       {"http://b.com/disallowed.html", "http://b.com"},
+       {true, true} /* throttled */,
+       0 /* creative_index */,
+       false /* should_paint */,
+       OriginStatusWithThrottling::kUnknownAndThrottled},
+      {"http://a.com",
+       {"http://a.com/disallowed.html", "http://b.com"},
+       {false, true} /* throttled */,
+       1 /* creative_index */,
+       false /* should_paint */,
+       OriginStatusWithThrottling::kUnknownAndUnthrottled},
+      {"http://a.com",
+       {"http://a.com/disallowed.html", "http://b.com"},
+       {true, true} /* throttled */,
+       1 /* creative_index */,
+       false /* should_paint */,
+       OriginStatusWithThrottling::kUnknownAndThrottled},
+      {"http://a.com",
+       {"http://a.com/disallowed.html", "http://b.com"},
+       {true, false} /* throttled */,
+       1 /* creative_index */,
+       false /* should_paint */,
+       OriginStatusWithThrottling::kUnknownAndThrottled},
+      {"http://a.com",
+       {"http://b.com/disallowed.html", "http://b.com"},
+       {true, false} /* throttled */,
+       1 /* creative_index */,
+       false /* should_paint */,
+       OriginStatusWithThrottling::kUnknownAndThrottled},
+      {"http://a.com",
+       {"http://b.com/disallowed.html", "http://a.com"},
+       {true, false} /* throttled */,
+       1 /* creative_index */,
+       true /* should_paint */,
+       OriginStatusWithThrottling::kSameAndUnthrottled},
+      {"http://a.com",
+       {"http://a.com/disallowed.html", "http://b.com"},
+       {true, false} /* throttled */,
+       1 /* creative_index */,
+       true /* should_paint */,
+       OriginStatusWithThrottling::kCrossAndUnthrottled}};
 
   for (const auto& creative_origin_test : test_cases) {
     TestCreativeOriginStatusWithThrottling(creative_origin_test);
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc
index cadccfa..9610764 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc
@@ -322,7 +322,7 @@
     if (!first_eligible_to_paint_.has_value() ||
         first_eligible_to_paint_.value() > time_stamp.value())
       first_eligible_to_paint_ = time_stamp;
-  } else if (!FirstContentfulPaint().has_value()) {
+  } else if (!earliest_first_contentful_paint_.has_value()) {
     // If a frame in this ad frame tree has already painted, there is no
     // further need to update paint eligibility. But if nothing has
     // painted and a null value is passed into the setter, that means the
@@ -332,6 +332,19 @@
   }
 }
 
+bool FrameData::SetEarliestFirstContentfulPaint(
+    base::Optional<base::TimeDelta> time_stamp) {
+  if (!time_stamp.has_value())
+    return false;
+
+  if (earliest_first_contentful_paint_.has_value() &&
+      time_stamp.value() >= earliest_first_contentful_paint_.value())
+    return false;
+
+  earliest_first_contentful_paint_ = time_stamp;
+  return true;
+}
+
 void FrameData::UpdateFrameVisibility() {
   visibility_ =
       !is_display_none_ &&
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h
index 4fd6937..8869f84f 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h
@@ -216,6 +216,10 @@
     return first_eligible_to_paint_;
   }
 
+  base::Optional<base::TimeDelta> earliest_first_contentful_paint() const {
+    return earliest_first_contentful_paint_;
+  }
+
   size_t bytes() const { return bytes_; }
 
   size_t network_bytes() const { return network_bytes_; }
@@ -260,6 +264,10 @@
 
   void SetFirstEligibleToPaint(base::Optional<base::TimeDelta> time_stamp);
 
+  // Returns whether a new FCP is set.
+  bool SetEarliestFirstContentfulPaint(
+      base::Optional<base::TimeDelta> time_stamp);
+
   HeavyAdStatus heavy_ad_status() const { return heavy_ad_status_; }
 
   HeavyAdStatus heavy_ad_status_with_noise() const {
@@ -357,6 +365,10 @@
   // timestamp and the implied throttling status are best-effort.
   base::Optional<base::TimeDelta> first_eligible_to_paint_;
 
+  // The smallest FCP seen for any any frame in this ad frame tree, if a
+  // frame has painted.
+  base::Optional<base::TimeDelta> earliest_first_contentful_paint_;
+
   // Indicates whether or not this frame met the criteria for the heavy ad
   // intervention.
   HeavyAdStatus heavy_ad_status_;
diff --git a/chrome/browser/performance_monitor/process_monitor.cc b/chrome/browser/performance_monitor/process_monitor.cc
index dd8797f9..d3bb80d 100644
--- a/chrome/browser/performance_monitor/process_monitor.cc
+++ b/chrome/browser/performance_monitor/process_monitor.cc
@@ -18,6 +18,7 @@
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/content_constants.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/buildflags/buildflags.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/plugins/flash_deprecation_infobar_delegate.cc b/chrome/browser/plugins/flash_deprecation_infobar_delegate.cc
index 1c9bcd8..dbca32d 100644
--- a/chrome/browser/plugins/flash_deprecation_infobar_delegate.cc
+++ b/chrome/browser/plugins/flash_deprecation_infobar_delegate.cc
@@ -62,7 +62,7 @@
   // informative in that case.
   const base::Time last_dismissal =
       profile->GetPrefs()->GetTime(prefs::kPluginsDeprecationInfobarLastShown);
-  return (base::Time::Now() - last_dismissal) > base::TimeDelta::FromDays(14);
+  return (base::Time::Now() - last_dismissal) > base::TimeDelta::FromDays(3);
 }
 
 FlashDeprecationInfoBarDelegate::FlashDeprecationInfoBarDelegate(
diff --git a/chrome/browser/plugins/plugin_utils.cc b/chrome/browser/plugins/plugin_utils.cc
index f9c8096..a3fec7c2 100644
--- a/chrome/browser/plugins/plugin_utils.cc
+++ b/chrome/browser/plugins/plugin_utils.cc
@@ -19,7 +19,6 @@
 #include "components/prefs/pref_service.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_util.h"
-#include "extensions/browser/info_map.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest_handlers/mime_types_handler.h"
diff --git a/chrome/browser/policy/chrome_browser_cloud_management_controller.cc b/chrome/browser/policy/chrome_browser_cloud_management_controller.cc
index 2b8de96..9053f09 100644
--- a/chrome/browser/policy/chrome_browser_cloud_management_controller.cc
+++ b/chrome/browser/policy/chrome_browser_cloud_management_controller.cc
@@ -640,7 +640,8 @@
           ->GetSharedURLLoaderFactory(),
       CloudPolicyClient::DeviceDMTokenCallback());
   cloud_policy_client_->AddObserver(this);
-  auto generator = std::make_unique<enterprise_reporting::ReportGenerator>();
+  auto generator = std::make_unique<enterprise_reporting::ReportGenerator>(
+      &delegate_factory_);
   report_scheduler_ = std::make_unique<enterprise_reporting::ReportScheduler>(
       cloud_policy_client_.get(), std::move(generator));
 
diff --git a/chrome/browser/policy/chrome_browser_cloud_management_controller.h b/chrome/browser/policy/chrome_browser_cloud_management_controller.h
index 47e7519..cfa068b 100644
--- a/chrome/browser/policy/chrome_browser_cloud_management_controller.h
+++ b/chrome/browser/policy/chrome_browser_cloud_management_controller.h
@@ -13,6 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
+#include "chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 
 class DeviceIdentityProvider;
@@ -153,6 +154,11 @@
 
   base::ObserverList<Observer, true>::Unchecked observers_;
 
+  // TODO(crbug.com/1092442): Move the delegate factory ownership to
+  // ChromeBrowserCloudManagementController's delegate after CBCMController has
+  // been split into delegates and moved to components.
+  enterprise_reporting::ReportingDelegateFactoryDesktop delegate_factory_;
+
   std::unique_ptr<ChromeBrowserCloudManagementRegistrar>
       cloud_management_registrar_;
   std::unique_ptr<MachineLevelUserCloudPolicyFetcher> policy_fetcher_;
diff --git a/chrome/browser/process_resource_usage.cc b/chrome/browser/process_resource_usage.cc
index b3eb3de..ef176eb 100644
--- a/chrome/browser/process_resource_usage.cc
+++ b/chrome/browser/process_resource_usage.cc
@@ -28,7 +28,9 @@
 void ProcessResourceUsage::RunPendingRefreshCallbacks() {
   DCHECK(thread_checker_.CalledOnValidThread());
   auto task_runner = base::ThreadTaskRunnerHandle::Get();
-  for (auto& callback : refresh_callbacks_)
+  base::circular_deque<base::OnceClosure> callbacks;
+  std::swap(callbacks, refresh_callbacks_);
+  for (auto& callback : callbacks)
     task_runner->PostTask(FROM_HERE, std::move(callback));
 }
 
diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
index fdcd022..11c1c78 100644
--- a/chrome/browser/process_singleton.h
+++ b/chrome/browser/process_singleton.h
@@ -103,8 +103,8 @@
   // handled within the current browser instance or false if the remote process
   // should handle it (i.e., because the current process is shutting down).
   using NotificationCallback =
-      base::Callback<bool(const base::CommandLine& command_line,
-                          const base::FilePath& current_directory)>;
+      base::RepeatingCallback<bool(const base::CommandLine& command_line,
+                                   const base::FilePath& current_directory)>;
 
   ProcessSingleton(const base::FilePath& user_data_dir,
                    const NotificationCallback& notification_callback);
@@ -138,7 +138,7 @@
 #if defined(OS_WIN)
   // Called to query whether to kill a hung browser process that has visible
   // windows. Return true to allow killing the hung process.
-  using ShouldKillRemoteProcessCallback = base::Callback<bool()>;
+  using ShouldKillRemoteProcessCallback = base::RepeatingCallback<bool()>;
   void OverrideShouldKillRemoteProcessCallbackForTesting(
       const ShouldKillRemoteProcessCallback& display_dialog_callback);
 #endif
@@ -164,7 +164,7 @@
       const base::TimeDelta& timeout);
   void OverrideCurrentPidForTesting(base::ProcessId pid);
   void OverrideKillCallbackForTesting(
-      const base::Callback<void(int)>& callback);
+      const base::RepeatingCallback<void(int)>& callback);
 #endif
 
  private:
@@ -204,7 +204,7 @@
 
   // Function to call when the other process is hung and needs to be killed.
   // Allows overriding for tests.
-  base::Callback<void(int)> kill_callback_;
+  base::RepeatingCallback<void(int)> kill_callback_;
 
   // Path in file system to the socket.
   base::FilePath socket_path_;
diff --git a/chrome/browser/process_singleton_modal_dialog_lock.cc b/chrome/browser/process_singleton_modal_dialog_lock.cc
index 7bbc521..5dc43ea0 100644
--- a/chrome/browser/process_singleton_modal_dialog_lock.cc
+++ b/chrome/browser/process_singleton_modal_dialog_lock.cc
@@ -15,14 +15,15 @@
 ProcessSingletonModalDialogLock::~ProcessSingletonModalDialogLock() {}
 
 void ProcessSingletonModalDialogLock::SetModalDialogNotificationHandler(
-    base::Closure notification_handler) {
+    base::RepeatingClosure notification_handler) {
   notification_handler_ = std::move(notification_handler);
 }
 
 ProcessSingleton::NotificationCallback
 ProcessSingletonModalDialogLock::AsNotificationCallback() {
-  return base::Bind(&ProcessSingletonModalDialogLock::NotificationCallbackImpl,
-                    base::Unretained(this));
+  return base::BindRepeating(
+      &ProcessSingletonModalDialogLock::NotificationCallbackImpl,
+      base::Unretained(this));
 }
 
 bool ProcessSingletonModalDialogLock::NotificationCallbackImpl(
diff --git a/chrome/browser/process_singleton_modal_dialog_lock.h b/chrome/browser/process_singleton_modal_dialog_lock.h
index ac1c566..318e2a3 100644
--- a/chrome/browser/process_singleton_modal_dialog_lock.h
+++ b/chrome/browser/process_singleton_modal_dialog_lock.h
@@ -31,7 +31,8 @@
 
   // Receives a callback to be run to close the active modal dialog, or an empty
   // closure if the active dialog is dismissed.
-  void SetModalDialogNotificationHandler(base::Closure notification_handler);
+  void SetModalDialogNotificationHandler(
+      base::RepeatingClosure notification_handler);
 
   // Returns the ProcessSingleton::NotificationCallback.
   // The callback is only valid during the lifetime of the
@@ -42,7 +43,7 @@
   bool NotificationCallbackImpl(const base::CommandLine& command_line,
                                 const base::FilePath& current_directory);
 
-  base::Closure notification_handler_;
+  base::RepeatingClosure notification_handler_;
   ProcessSingleton::NotificationCallback original_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(ProcessSingletonModalDialogLock);
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
index 6eb01af..e693f20b 100644
--- a/chrome/browser/process_singleton_posix.cc
+++ b/chrome/browser/process_singleton_posix.cc
@@ -478,8 +478,8 @@
       DCHECK_CURRENTLY_ON(BrowserThread::IO);
       // Wait for reads.
       fd_watch_controller_ = base::FileDescriptorWatcher::WatchReadable(
-          fd, base::Bind(&SocketReader::OnSocketCanReadWithoutBlocking,
-                         base::Unretained(this)));
+          fd, base::BindRepeating(&SocketReader::OnSocketCanReadWithoutBlocking,
+                                  base::Unretained(this)));
       // If we haven't completed in a reasonable amount of time, give up.
       timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kTimeoutInSeconds),
                    this, &SocketReader::CleanupAndDeleteSelf);
@@ -590,8 +590,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // Watch for client connections on this socket.
   socket_watcher_ = base::FileDescriptorWatcher::WatchReadable(
-      socket, base::Bind(&LinuxWatcher::OnSocketCanReadWithoutBlocking,
-                         base::Unretained(this), socket));
+      socket, base::BindRepeating(&LinuxWatcher::OnSocketCanReadWithoutBlocking,
+                                  base::Unretained(this), socket));
 }
 
 void ProcessSingleton::LinuxWatcher::HandleMessage(
@@ -719,8 +719,8 @@
   lock_path_ = user_data_dir.Append(chrome::kSingletonLockFilename);
   cookie_path_ = user_data_dir.Append(chrome::kSingletonCookieFilename);
 
-  kill_callback_ = base::Bind(&ProcessSingleton::KillProcess,
-                              base::Unretained(this));
+  kill_callback_ = base::BindRepeating(&ProcessSingleton::KillProcess,
+                                       base::Unretained(this));
 }
 
 ProcessSingleton::~ProcessSingleton() {
@@ -944,7 +944,7 @@
 }
 
 void ProcessSingleton::OverrideKillCallbackForTesting(
-    const base::Callback<void(int)>& callback) {
+    const base::RepeatingCallback<void(int)>& callback) {
   kill_callback_ = callback;
 }
 
diff --git a/chrome/browser/process_singleton_posix_unittest.cc b/chrome/browser/process_singleton_posix_unittest.cc
index 86173e4..e18e6d8 100644
--- a/chrome/browser/process_singleton_posix_unittest.cc
+++ b/chrome/browser/process_singleton_posix_unittest.cc
@@ -47,10 +47,10 @@
   class TestableProcessSingleton : public ProcessSingleton {
    public:
     explicit TestableProcessSingleton(const base::FilePath& user_data_dir)
-        : ProcessSingleton(
-            user_data_dir,
-            base::Bind(&TestableProcessSingleton::NotificationCallback,
-                       base::Unretained(this))) {}
+        : ProcessSingleton(user_data_dir,
+                           base::BindRepeating(
+                               &TestableProcessSingleton::NotificationCallback,
+                               base::Unretained(this))) {}
 
     std::vector<base::CommandLine::StringVector> callback_command_lines_;
 
@@ -175,9 +175,8 @@
     if (override_kill) {
       process_singleton->OverrideCurrentPidForTesting(
           base::GetCurrentProcId() + 1);
-      process_singleton->OverrideKillCallbackForTesting(
-          base::Bind(&ProcessSingletonPosixTest::KillCallback,
-                     base::Unretained(this)));
+      process_singleton->OverrideKillCallbackForTesting(base::BindRepeating(
+          &ProcessSingletonPosixTest::KillCallback, base::Unretained(this)));
     }
 
     return process_singleton->NotifyOtherProcessWithTimeout(
diff --git a/chrome/browser/process_singleton_startup_lock.cc b/chrome/browser/process_singleton_startup_lock.cc
index 58d27a7..b20bfb00 100644
--- a/chrome/browser/process_singleton_startup_lock.cc
+++ b/chrome/browser/process_singleton_startup_lock.cc
@@ -18,8 +18,9 @@
 
 ProcessSingleton::NotificationCallback
 ProcessSingletonStartupLock::AsNotificationCallback() {
-  return base::Bind(&ProcessSingletonStartupLock::NotificationCallbackImpl,
-                    base::Unretained(this));
+  return base::BindRepeating(
+      &ProcessSingletonStartupLock::NotificationCallbackImpl,
+      base::Unretained(this));
 }
 
 void ProcessSingletonStartupLock::Unlock() {
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
index 96f84b8..055209a 100644
--- a/chrome/browser/process_singleton_win.cc
+++ b/chrome/browser/process_singleton_win.cc
@@ -267,8 +267,7 @@
       lock_file_(INVALID_HANDLE_VALUE),
       user_data_dir_(user_data_dir),
       should_kill_remote_process_callback_(
-          base::Bind(&DisplayShouldKillMessageBox)) {
-}
+          base::BindRepeating(&DisplayShouldKillMessageBox)) {}
 
 ProcessSingleton::~ProcessSingleton() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -415,9 +414,10 @@
       if (lock_file_ != INVALID_HANDLE_VALUE) {
         // Set the window's title to the path of our user data directory so
         // other Chrome instances can decide if they should forward to us.
-        bool result = window_.CreateNamed(
-            base::Bind(&ProcessLaunchNotification, notification_callback_),
-            user_data_dir_.value());
+        bool result =
+            window_.CreateNamed(base::BindRepeating(&ProcessLaunchNotification,
+                                                    notification_callback_),
+                                user_data_dir_.value());
         CHECK(result && window_.hwnd());
       }
     }
diff --git a/chrome/browser/process_singleton_win_unittest.cc b/chrome/browser/process_singleton_win_unittest.cc
index 1c48dd4..c84bfb8 100644
--- a/chrome/browser/process_singleton_win_unittest.cc
+++ b/chrome/browser/process_singleton_win_unittest.cc
@@ -126,8 +126,8 @@
   }
 
   // Instantiate the process singleton.
-  ProcessSingleton process_singleton(user_data_dir,
-                                     base::Bind(&NotificationCallback));
+  ProcessSingleton process_singleton(
+      user_data_dir, base::BindRepeating(&NotificationCallback));
 
   if (!process_singleton.Create())
     return kErrorResultCode;
@@ -223,11 +223,11 @@
     // The ready event has been signalled - the process singleton is held by
     // the hung sub process.
     test_singleton_.reset(new ProcessSingleton(
-        user_data_dir(), base::Bind(&NotificationCallback)));
+        user_data_dir(), base::BindRepeating(&NotificationCallback)));
 
     test_singleton_->OverrideShouldKillRemoteProcessCallbackForTesting(
-        base::Bind(&ProcessSingletonTest::MockShouldKillRemoteProcess,
-                   base::Unretained(this), allow_kill));
+        base::BindRepeating(&ProcessSingletonTest::MockShouldKillRemoteProcess,
+                            base::Unretained(this), allow_kill));
   }
 
   base::Process* browser_victim() { return &browser_victim_; }
diff --git a/chrome/browser/renderer_host/chrome_extension_message_filter.cc b/chrome/browser/renderer_host/chrome_extension_message_filter.cc
index 49ee403..5f0b0b3 100644
--- a/chrome/browser/renderer_host/chrome_extension_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_extension_message_filter.cc
@@ -63,9 +63,7 @@
                            base::size(kExtensionFilteredMessageClasses)),
       render_process_id_(render_process_id),
       profile_(profile),
-      activity_log_(extensions::ActivityLog::GetInstance(profile)),
-      extension_info_map_(
-          extensions::ExtensionSystem::Get(profile)->info_map()) {
+      activity_log_(extensions::ActivityLog::GetInstance(profile)) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observed_profiles_.Add(profile);
 }
diff --git a/chrome/browser/renderer_host/chrome_extension_message_filter.h b/chrome/browser/renderer_host/chrome_extension_message_filter.h
index 6ac98153..065b3ce6 100644
--- a/chrome/browser/renderer_host/chrome_extension_message_filter.h
+++ b/chrome/browser/renderer_host/chrome_extension_message_filter.h
@@ -22,7 +22,6 @@
 
 namespace extensions {
 class ActivityLog;
-class InfoMap;
 struct Message;
 }
 
@@ -88,8 +87,6 @@
   // access on the UI thread, and may be null.
   extensions::ActivityLog* activity_log_;
 
-  scoped_refptr<extensions::InfoMap> extension_info_map_;
-
   ScopedObserver<Profile, ProfileObserver> observed_profiles_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ChromeExtensionMessageFilter);
diff --git a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
index 8bff4bc..35128d3 100644
--- a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
@@ -999,7 +999,8 @@
 #define MAYBE_DiscardTabsWithOccludedWindow \
   DISABLED_DiscardTabsWithOccludedWindow
 #else
-#define MAYBE_DiscardTabsWithOccludedWindow DiscardTabsWithOccludedWindow
+// TODO(https://crbug.com/1106485): The test is flaky on release builds.
+#define MAYBE_DiscardTabsWithOccludedWindow DISABLED_DiscardTabsWithOccludedWindow
 #endif
 IN_PROC_BROWSER_TEST_F(TabManagerTest, MAYBE_DiscardTabsWithOccludedWindow) {
   // Occluded browser.
diff --git a/chrome/browser/resources/chromeos/accessibility/common/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/common/BUILD.gn
index 6300c81..00c4008 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/common/BUILD.gn
@@ -30,6 +30,7 @@
     "closure_shim.js",
     "constants.js",
     "repeated_event_handler.js",
+    "repeated_tree_change_handler.js",
     "tree_walker.js",
   ]
   rewrite_rules = [ rebase_path(".", root_build_dir) + ":" ]
@@ -83,6 +84,10 @@
   externs_list = [ "$externs_path/automation.js" ]
 }
 
+js_library("repeated_tree_change_handler") {
+  externs_list = [ "$externs_path/automation.js" ]
+}
+
 source_set("browser_tests") {
   testonly = true
   assert(enable_extensions)
@@ -114,6 +119,7 @@
     "array_util_test.js",
     "automation_util_test.js",
     "repeated_event_handler_test.js",
+    "repeated_tree_change_handler_test.js",
     "tree_walker_test.js",
   ]
   gen_include_files = [
diff --git a/chrome/browser/resources/chromeos/accessibility/common/repeated_event_handler.js b/chrome/browser/resources/chromeos/accessibility/common/repeated_event_handler.js
index 0b277208..d60d71b 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/repeated_event_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/repeated_event_handler.js
@@ -8,19 +8,37 @@
  */
 class RepeatedEventHandler {
   /**
-   * @param {!chrome.automation.AutomationNode} node
+   * @param {!chrome.automation.AutomationNode |
+   *     !Array<!chrome.automation.AutomationNode>} nodes
    * @param {!chrome.automation.EventType} type
    * @param {!function(!chrome.automation.AutomationEvent)} callback
-   * @param {boolean} exact_match Whether to ignore events where the target is
-   *     not the provided node.
-   * @param {boolean} capture
+   * @param {{exactMatch: (boolean|undefined), capture: (boolean|undefined),
+   *     allAncestors: (boolean|undefined)}} options
+   *    exactMatch True if events should only be handled if the provided node is
+   *        the target.
+   *    capture True if events for children of |node| should be handled before
+   *        they reach the target node; false to be handled after the target
+   *        node.
+   *    allAncestors True if a listener should be added to all ancestors of the
+   *        provided nodes.
    */
-  constructor(node, type, callback, exact_match = false, capture = false) {
+  constructor(nodes, type, callback, options = {}) {
     /** @private {!Array<!chrome.automation.AutomationEvent>} */
     this.eventStack_ = [];
 
-    /** @private {!chrome.automation.AutomationNode} */
-    this.node_ = node;
+    /** @private {!Array<chrome.automation.AutomationNode>} */
+    this.nodes_ = nodes instanceof Array ? nodes : [nodes];
+
+    if (options.allAncestors) {
+      nodes = this.nodes_;  // Make sure nodes is an array.
+      this.nodes_ = [];
+      for (let node of nodes) {
+        while (node) {
+          this.nodes_.push(node);
+          node = node.parent;
+        }
+      }
+    }
 
     /** @private {!chrome.automation.EventType} */
     this.type_ = type;
@@ -29,20 +47,24 @@
     this.callback_ = callback;
 
     /** @private {boolean} */
-    this.exact_match_ = exact_match;
+    this.exactMatch_ = options.exactMatch || false;
 
     /** @private {boolean} */
-    this.capture_ = capture;
+    this.capture_ = options.capture || false;
 
     /** @private {!function(!chrome.automation.AutomationEvent)} */
     this.handler_ = this.onEvent_.bind(this);
 
-    node.addEventListener(type, this.handler_, capture);
+    for (const node of this.nodes_) {
+      node.addEventListener(this.type_, this.handler_, this.capture_);
+    }
   }
 
   /** Stops listening or handling future events. */
   stopListening() {
-    this.node_.removeEventListener(this.type_, this.handler_, this.capture_);
+    for (const node of this.nodes_) {
+      node.removeEventListener(this.type_, this.handler_, this.capture_);
+    }
   }
 
   /**
@@ -61,7 +83,7 @@
     }
 
     const event = this.eventStack_.pop();
-    if (this.exact_match_ && event.target !== this.node_) {
+    if (this.exactMatch_ && !this.nodes_.includes(event.target)) {
       return;
     }
     this.eventStack_ = [];
diff --git a/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler.js b/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler.js
new file mode 100644
index 0000000..6042143
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler.js
@@ -0,0 +1,59 @@
+// 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.
+
+/**
+ * This class assists with processing repeated tree changes in nontrivial ways
+ * by allowing only the most recent tree change to be processed.
+ */
+class RepeatedTreeChangeHandler {
+  /**
+   * @param {!chrome.automation.TreeChangeObserverFilter} filter
+   * @param {!function(!chrome.automation.TreeChange)} callback
+   * @param {{predicate: (function(!chrome.automation.TreeChange): boolean)}}
+   *     options
+   *         predicate A generic predicate that filters for changes of interest.
+   */
+  constructor(filter, callback, options = {}) {
+    /** @private {!Array<!chrome.automation.TreeChange>} */
+    this.changeStack_ = [];
+
+    /** @private {!function(!chrome.automation.TreeChange)} */
+    this.callback_ = callback;
+
+    /**
+     * A predicate for which tree changes are of interest. If none is provided,
+     * default to always return true.
+     * @private {!function(!chrome.automation.TreeChange)}
+     */
+    this.predicate_ = options.predicate || ((c) => true);
+
+    /** @private {!function(!chrome.automation.TreeChange)} */
+    this.handler_ = this.onChange_.bind(this);
+
+    chrome.automation.addTreeChangeObserver(filter, this.handler_);
+  }
+
+  /**
+   * @param {!chrome.automation.TreeChange} change
+   * @private
+   */
+  onChange_(change) {
+    if (this.predicate_(change)) {
+      this.changeStack_.push(change);
+      setTimeout(this.handleChange_.bind(this), 0);
+    }
+  }
+
+  /** @private */
+  handleChange_() {
+    if (this.changeStack_.length === 0) {
+      return;
+    }
+
+    const change = this.changeStack_.pop();
+    this.changeStack_ = [];
+
+    this.callback_(change);
+  }
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler_test.js b/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler_test.js
new file mode 100644
index 0000000..982dc2b
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler_test.js
@@ -0,0 +1,61 @@
+// 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 test fixture.
+GEN_INCLUDE([
+  '../chromevox/testing/chromevox_next_e2e_test_base.js',
+  'repeated_tree_change_handler.js'
+]);
+
+/** Test fixture for array_util.js. */
+RepeatedTreeChangeHandlerTest = class extends ChromeVoxNextE2ETest {};
+
+TEST_F(
+    'RepeatedTreeChangeHandlerTest', 'RepeatedTreeChangeHandledOnce',
+    function() {
+      this.runWithLoadedTree('', (root) => {
+        this.handlerCallCount = 0;
+        const handler = () => this.handlerCallCount++;
+
+        const repeatedHandler =
+            new RepeatedTreeChangeHandler('allTreeChanges', handler);
+
+        // Simulate events being fired.
+        repeatedHandler.onChange_();
+        repeatedHandler.onChange_();
+        repeatedHandler.onChange_();
+        repeatedHandler.onChange_();
+        repeatedHandler.onChange_();
+
+        // Yield before verifying how many times the handler was called.
+        setTimeout(() => assertEquals(this.handlerCallCount, 1), 0);
+      });
+    });
+
+TEST_F('RepeatedTreeChangeHandlerTest', 'Predicate', function() {
+  this.runWithLoadedTree('', (root) => {
+    this.handlerCallCount = 0;
+    const handler = () => this.handlerCallCount++;
+
+    const repeatedHandler = new RepeatedTreeChangeHandler(
+        'allTreeChanges', handler,
+        {predicate: (c) => c.type === 'nodeRemoved'});
+
+    // Simulate events being fired.
+    repeatedHandler.onChange_({type: 'nodeAdded'});
+    repeatedHandler.onChange_({type: 'nodeAdded'});
+    repeatedHandler.onChange_({type: 'nodeAdded'});
+    repeatedHandler.onChange_({type: 'nodeRemoved'});
+    repeatedHandler.onChange_({type: 'nodeRemoved'});
+    repeatedHandler.onChange_({type: 'nodeRemoved'});
+    repeatedHandler.onChange_({type: 'nodeRemoved'});
+
+    // Verify that nodes that don't satisfy the predicate aren't added to the
+    // change stack.
+    assertEquals(repeatedHandler.changeStack_.length, 4);
+
+    // Yield before verifying how many times the handler was called.
+    setTimeout(() => assertEquals(this.handlerCallCount, 1), 0);
+  });
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js
index c2dfec1..0e6a1b0 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js
@@ -92,7 +92,8 @@
       this.locationChangedHandler_ = new RepeatedEventHandler(
           this.group_.automationNode,
           chrome.automation.EventType.LOCATION_CHANGED,
-          () => FocusRingManager.setFocusedNode(this), true /* exact_match */);
+          () => FocusRingManager.setFocusedNode(this),
+          {exactMatch: true, allAncestors: true});
     }
   }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper.js
index 0bd655b..b7b0482 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper.js
@@ -123,7 +123,8 @@
     super.onFocus();
     this.locationChangedHandler_ = new RepeatedEventHandler(
         this.baseNode_, chrome.automation.EventType.LOCATION_CHANGED,
-        () => FocusRingManager.setFocusedNode(this), true /* exact_match */);
+        () => FocusRingManager.setFocusedNode(this),
+        {exactMatch: true, allAncestors: true});
   }
 
   /** @override */
diff --git a/chrome/browser/resources/pdf/elements/viewer-download-controls.html b/chrome/browser/resources/pdf/elements/viewer-download-controls.html
index 0776ec9..32976fd 100644
--- a/chrome/browser/resources/pdf/elements/viewer-download-controls.html
+++ b/chrome/browser/resources/pdf/elements/viewer-download-controls.html
@@ -1,4 +1,9 @@
-<style include="pdf-shared"></style>
+<style include="pdf-shared">
+  cr-action-menu::part(dialog) {
+    position: fixed;
+    top: 48px;
+  }
+</style>
 <cr-icon-button id="download" iron-icon="cr:file-download"
     on-click="onDownloadClick_" aria-label$="$i18n{tooltipDownload}"
     aria-haspopup$="[[downloadHasPopup_]]"
diff --git a/chrome/browser/resources/pdf/elements/viewer-download-controls.js b/chrome/browser/resources/pdf/elements/viewer-download-controls.js
index 3ab50b7..98de0b392 100644
--- a/chrome/browser/resources/pdf/elements/viewer-download-controls.js
+++ b/chrome/browser/resources/pdf/elements/viewer-download-controls.js
@@ -107,8 +107,6 @@
   showDownloadMenu_() {
     this.getDownloadMenu_().showAt(this.$.download, {
       anchorAlignmentX: AnchorAlignment.CENTER,
-      anchorAlignmentY: AnchorAlignment.AFTER_END,
-      noOffset: true,
     });
     // For tests
     this.dispatchEvent(new CustomEvent(
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp b/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
index 0546b448..7f744ed 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
+++ b/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
@@ -257,6 +257,11 @@
            use_base_dir="false"
            compress="false"
            type="BINDATA" />
+  <include name="IDR_OS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_DIALOG_M_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/nearby_share_page/nearby_share_device_name_dialog.m.js"
+           use_base_dir="false"
+           compress="false"
+           type="BINDATA" />
   <include name="IDR_OS_SETTINGS_ROUTER_M_JS"
            file="${root_gen_dir}/chrome/browser/resources/settings/router.m.js"
            use_base_dir="false"
diff --git a/chrome/browser/resources/settings/nearby_share_page/BUILD.gn b/chrome/browser/resources/settings/nearby_share_page/BUILD.gn
index 81c2a50..8eea4c7 100644
--- a/chrome/browser/resources/settings/nearby_share_page/BUILD.gn
+++ b/chrome/browser/resources/settings/nearby_share_page/BUILD.gn
@@ -7,11 +7,27 @@
 import("../settings.gni")
 
 js_type_check("closure_compile") {
-  deps = [ ":nearby_share_subpage" ]
+  deps = [
+    ":nearby_share_device_name_dialog",
+    ":nearby_share_subpage",
+  ]
+}
+
+js_library("nearby_share_device_name_dialog") {
+  deps = [
+    "../prefs",
+    "../prefs:prefs_behavior",
+    "../prefs:prefs_types",
+    "//ui/webui/resources/cr_elements/cr_button:cr_button",
+    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
+    "//ui/webui/resources/cr_elements/cr_input:cr_input",
+    "//ui/webui/resources/js:i18n_behavior",
+  ]
 }
 
 js_library("nearby_share_subpage") {
   deps = [
+    ":nearby_share_device_name_dialog",
     "../prefs",
     "../prefs:prefs_behavior",
     "../prefs:prefs_types",
@@ -22,20 +38,39 @@
 
 group("polymer3_elements") {
   public_deps = [
+    ":nearby_share_device_name_dialog_module",
+    ":nearby_share_subpage_module",
     "../controls:polymer3_elements",
     "../prefs:polymer3_elements",
-    ":nearby_share_subpage_module",
   ]
 }
 
 js_type_check("closure_compile_module") {
   is_polymer3 = true
-  deps = [ ":nearby_share_subpage.m" ]
+  deps = [
+    ":nearby_share_device_name_dialog.m",
+    ":nearby_share_subpage.m",
+  ]
+}
+
+js_library("nearby_share_device_name_dialog.m") {
+  sources = [ "$root_gen_dir/chrome/browser/resources/settings/nearby_share_page/nearby_share_device_name_dialog.m.js" ]
+  deps = [
+    "../prefs:prefs.m",
+    "../prefs:prefs_behavior.m",
+    "../prefs:prefs_types.m",
+    "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
+    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
+    "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
+  ]
+  extra_deps = [ ":nearby_share_device_name_dialog_module" ]
 }
 
 js_library("nearby_share_subpage.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/nearby_share_page/nearby_share_subpage.m.js" ]
   deps = [
+    ":nearby_share_device_name_dialog.m",
     "../prefs:prefs.m",
     "../prefs:prefs_behavior.m",
     "../prefs:prefs_types.m",
@@ -45,6 +80,14 @@
   extra_deps = [ ":nearby_share_subpage_module" ]
 }
 
+polymer_modulizer("nearby_share_device_name_dialog") {
+  js_file = "nearby_share_device_name_dialog.js"
+  html_file = "nearby_share_device_name_dialog.html"
+  html_type = "dom-module"
+  namespace_rewrites = settings_namespace_rewrites
+  auto_imports = settings_auto_imports
+}
+
 polymer_modulizer("nearby_share_subpage") {
   js_file = "nearby_share_subpage.js"
   html_file = "nearby_share_subpage.html"
diff --git a/chrome/browser/resources/settings/nearby_share_page/nearby_share_device_name_dialog.html b/chrome/browser/resources/settings/nearby_share_page/nearby_share_device_name_dialog.html
new file mode 100644
index 0000000..e43401e9
--- /dev/null
+++ b/chrome/browser/resources/settings/nearby_share_page/nearby_share_device_name_dialog.html
@@ -0,0 +1,35 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="../prefs/prefs.html">
+<link rel="import" href="../prefs/prefs_behavior.html">
+
+<dom-module id="nearby-share-device-name-dialog">
+  <template>
+    <style include="iron-flex"></style>
+
+    <cr-dialog id="dialog">
+      <div slot="title">
+        $i18n{nearbyShareDeviceNameDialogTitle}
+      </div>
+      <div slot="body">
+        <cr-input label="$i18n{nearbyShareDeviceNameDialogInputLabel}"
+            value="[[prefs.nearby_sharing.device_name.value]]" autofocus>
+        </cr-input>
+      </div>
+      <div class="layout horizontal center" slot="button-container">
+        <cr-button class="cancel-button" on-click="onCancelTap_">
+          $i18n{cancel}
+        </cr-button>
+        <cr-button class="action-button" on-click="onDoneTap_">
+          $i18n{done}
+        </cr-button>
+      </div>
+    </cr-dialog>
+  </template>
+  <script src="nearby_share_device_name_dialog.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/nearby_share_page/nearby_share_device_name_dialog.js b/chrome/browser/resources/settings/nearby_share_page/nearby_share_device_name_dialog.js
new file mode 100644
index 0000000..e2d12be
--- /dev/null
+++ b/chrome/browser/resources/settings/nearby_share_page/nearby_share_device_name_dialog.js
@@ -0,0 +1,54 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'nearby-share-device-name-dialog' allows editing of the device display name
+ * when using Nearby Share.
+ */
+Polymer({
+  is: 'nearby-share-device-name-dialog',
+
+  behaviors: [
+    I18nBehavior,
+    PrefsBehavior,
+  ],
+
+  properties: {
+    /** Preferences state. */
+    prefs: {
+      type: Object,
+      notify: true,
+    },
+  },
+
+  attached() {
+    this.open();
+  },
+
+  open() {
+    const dialog = /** @type {!CrDialogElement} */ (this.$.dialog);
+    if (!dialog.open) {
+      dialog.showModal();
+    }
+  },
+
+  close() {
+    const dialog = /** @type {!CrDialogElement} */ (this.$.dialog);
+    if (dialog.open) {
+      dialog.close();
+    }
+  },
+
+  /** @private */
+  onCancelTap_() {
+    this.close();
+  },
+
+  /** @private */
+  onDoneTap_() {
+    this.setPrefValue('nearby_sharing.device_name', this.$$('cr-input').value);
+    this.close();
+  },
+});
diff --git a/chrome/browser/resources/settings/nearby_share_page/nearby_share_subpage.html b/chrome/browser/resources/settings/nearby_share_page/nearby_share_subpage.html
index 58cba20..44acd1e 100644
--- a/chrome/browser/resources/settings/nearby_share_page/nearby_share_subpage.html
+++ b/chrome/browser/resources/settings/nearby_share_page/nearby_share_subpage.html
@@ -7,6 +7,7 @@
 <link rel="import" href="../prefs/prefs.html">
 <link rel="import" href="../prefs/prefs_behavior.html">
 <link rel="import" href="../settings_shared_css.html">
+<link rel="import" href="nearby_share_device_name_dialog.html">
 
 <dom-module id="settings-nearby-share-subpage">
   <template>
@@ -22,6 +23,27 @@
           pref="{{prefs.nearby_sharing.enabled}}">
       </settings-toggle-button>
     </div>
+    <div class="settings-box two-line">
+      <div class="start">
+        <div role="heading" aria-hidden="true">
+          $i18n{nearbyShareDeviceNameRowTitle}
+        </div>
+        <div aria-hidden="true" class="secondary">
+          [[prefs.nearby_sharing.device_name.value]]
+        </div>
+      </div>
+      <div class="separator"></div>
+      <cr-button id="editDeviceNameButton" on-click="onDeviceNameTap_"
+          aria-description="[[getEditNameButtonAriaDescription_(
+              prefs.nearby_sharing.device_name.value)]]">
+        $i18n{nearbyShareEditDeviceName}
+      </cr-button>
+    </div>
+    <template is="dom-if" if="[[showDeviceNameDialog_]]" restamp>
+      <nearby-share-device-name-dialog prefs="{{prefs}}" id="deviceNameDialog"
+          on-close="onDeviceNameDialogClose_">
+      </nearby-share-device-name-dialog>
+    </template>
   </template>
   <script src="nearby_share_subpage.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/nearby_share_page/nearby_share_subpage.js b/chrome/browser/resources/settings/nearby_share_page/nearby_share_subpage.js
index cbd4716..7b460e6 100644
--- a/chrome/browser/resources/settings/nearby_share_page/nearby_share_subpage.js
+++ b/chrome/browser/resources/settings/nearby_share_page/nearby_share_subpage.js
@@ -22,6 +22,12 @@
       type: Object,
       notify: true,
     },
+
+    /** @private {boolean} */
+    showDeviceNameDialog_: {
+      type: Boolean,
+      value: false,
+    },
   },
 
   /**
@@ -36,6 +42,24 @@
   },
 
   /**
+   * @private
+   */
+  onDeviceNameTap_() {
+    if (this.showDeviceNameDialog_) {
+      return;
+    }
+    this.showDeviceNameDialog_ = true;
+  },
+
+  /**
+   * @param {!Event} event
+   * @private
+   */
+  onDeviceNameDialogClose_(event) {
+    this.showDeviceNameDialog_ = false;
+  },
+
+  /**
    * @param {boolean} state boolean state that determines which string to show
    * @param {string} onstr string to show when state is true
    * @param {string} offstr string to show when state is false
@@ -45,4 +69,13 @@
   getOnOffString_(state, onstr, offstr) {
     return state ? onstr : offstr;
   },
+
+  /**
+   * @param {string} name name of device
+   * @return {string} localized string
+   * @private
+   */
+  getEditNameButtonAriaDescription_(name) {
+    return this.i18n('nearbyShareDeviceNameAriaDescription', name);
+  },
 });
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index 031836c2..098af0698 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -1262,6 +1262,12 @@
       <structure name="IDR_OS_SETTINGS_MULTIDEVICE_TETHER_ITEM_JS"
                  file="chromeos/multidevice_page/multidevice_tether_item.js"
                  compress="false" type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_DIALOG_HTML"
+                 file="nearby_share_page/nearby_share_device_name_dialog.html"
+                 compress="false" type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_DIALOG_JS"
+                 file="nearby_share_page/nearby_share_device_name_dialog.js"
+                 compress="false" type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_NEARBY_SHARE_SUBPAGE_HTML"
                  file="nearby_share_page/nearby_share_subpage.html"
                  compress="false" type="chrome_html" />
diff --git a/chrome/browser/share/DEPS b/chrome/browser/share/DEPS
index 0e47ab2..440fe53 100644
--- a/chrome/browser/share/DEPS
+++ b/chrome/browser/share/DEPS
@@ -2,6 +2,7 @@
   # TODO(crbug/1022172): Remove this dependency when ShareActivity is moved to
   # chrome/browser/share.
   "+chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java",
+  "+chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/FileProviderHelper.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java",
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
index 7267a59..68642ad0 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
@@ -19,7 +19,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.Pref;
-import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfShareActivity;
 import org.chromium.chrome.browser.share.ChromeShareExtras;
 import org.chromium.chrome.browser.share.qrcode.QrCodeCoordinator;
@@ -31,6 +31,7 @@
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
 import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
 import org.chromium.components.browser_ui.share.ShareParams;
+import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.widget.Toast;
 
@@ -49,7 +50,6 @@
     private final Supplier<Tab> mTabProvider;
     private final BottomSheetController mBottomSheetController;
     private final ShareSheetBottomSheetContent mBottomSheetContent;
-    private final PrefServiceBridge mPrefServiceBridge;
     private final ShareParams mShareParams;
     private final Callback<Tab> mPrintTabCallback;
     private final long mShareStartTime;
@@ -66,8 +66,6 @@
      * @param bottomSheetController The {@link BottomSheetController} for the current activity.
      * @param bottomSheetContent The {@link ShareSheetBottomSheetContent} for the current
      * activity.
-     * @param prefServiceBridge The {@link PrefServiceBridge} singleton. This provides printing
-     * preferences.
      * @param shareParams The {@link ShareParams} for the current share.
      * @param chromeShareExtras The {@link ChromeShareExtras} for the current share.
      * @param printTab A {@link Callback} that will print a given Tab.
@@ -77,14 +75,13 @@
      */
     ChromeProvidedSharingOptionsProvider(Activity activity, Supplier<Tab> tabProvider,
             BottomSheetController bottomSheetController,
-            ShareSheetBottomSheetContent bottomSheetContent, PrefServiceBridge prefServiceBridge,
-            ShareParams shareParams, ChromeShareExtras chromeShareExtras, Callback<Tab> printTab,
-            long shareStartTime, ChromeOptionShareCallback chromeOptionShareCallback) {
+            ShareSheetBottomSheetContent bottomSheetContent, ShareParams shareParams,
+            ChromeShareExtras chromeShareExtras, Callback<Tab> printTab, long shareStartTime,
+            ChromeOptionShareCallback chromeOptionShareCallback) {
         mActivity = activity;
         mTabProvider = tabProvider;
         mBottomSheetController = bottomSheetController;
         mBottomSheetContent = bottomSheetContent;
-        mPrefServiceBridge = prefServiceBridge;
         mShareParams = shareParams;
         mPrintTabCallback = printTab;
         mShareStartTime = shareStartTime;
@@ -152,7 +149,8 @@
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_SHARE_QRCODE)) {
             mOrderedFirstPartyOptions.add(createQrCodeFirstPartyOption());
         }
-        if (mPrefServiceBridge.getBoolean(Pref.PRINTING_ENABLED)) {
+        if (UserPrefs.get(Profile.fromWebContents(mTabProvider.get().getWebContents()))
+                        .getBoolean(Pref.PRINTING_ENABLED)) {
             mOrderedFirstPartyOptions.add(createPrintingFirstPartyOption());
         }
     }
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
index 140edfb..62f87d9 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinator.java
@@ -15,7 +15,6 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.share.ChromeShareExtras;
 import org.chromium.chrome.browser.share.ShareHelper;
 import org.chromium.chrome.browser.tab.Tab;
@@ -41,7 +40,6 @@
     private final BottomSheetController mBottomSheetController;
     private final Supplier<Tab> mTabProvider;
     private final ShareSheetPropertyModelBuilder mPropertyModelBuilder;
-    private final PrefServiceBridge mPrefServiceBridge;
     private final Callback<Tab> mPrintTabCallback;
     private long mShareStartTime;
     private boolean mExcludeFirstParty;
@@ -55,17 +53,13 @@
      * @param controller The {@link BottomSheetController} for the current activity.
      * @param tabProvider Supplier for the current activity tab.
      * @param modelBuilder The {@link ShareSheetPropertyModelBuilder} for the share sheet.
-     * @param prefServiceBridge The {@link PrefServiceBridge} singleton. This provides preferences
-     * for the Chrome-provided property models.
      */
     // TODO(crbug/1022172): Should be package-protected once modularization is complete.
     public ShareSheetCoordinator(BottomSheetController controller, Supplier<Tab> tabProvider,
-            ShareSheetPropertyModelBuilder modelBuilder, PrefServiceBridge prefServiceBridge,
-            Callback<Tab> printTab) {
+            ShareSheetPropertyModelBuilder modelBuilder, Callback<Tab> printTab) {
         mBottomSheetController = controller;
         mTabProvider = tabProvider;
         mPropertyModelBuilder = modelBuilder;
-        mPrefServiceBridge = prefServiceBridge;
         mPrintTabCallback = printTab;
         mBottomSheetObserver = new EmptyBottomSheetObserver() {
             @Override
@@ -138,8 +132,8 @@
         }
         ChromeProvidedSharingOptionsProvider chromeProvidedSharingOptionsProvider =
                 new ChromeProvidedSharingOptionsProvider(activity, mTabProvider,
-                        mBottomSheetController, mBottomSheet, mPrefServiceBridge, shareParams,
-                        chromeShareExtras, mPrintTabCallback, mShareStartTime, this);
+                        mBottomSheetController, mBottomSheet, shareParams, chromeShareExtras,
+                        mPrintTabCallback, mShareStartTime, this);
 
         return chromeProvidedSharingOptionsProvider.getPropertyModels(contentTypes);
     }
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
index aa588fe..09f6b7b 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProviderTest.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.share.share_sheet;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.anyString;
 
 import android.app.Activity;
@@ -25,15 +26,21 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.profiles.ProfileJni;
 import org.chromium.chrome.browser.share.ChromeShareExtras;
 import org.chromium.chrome.browser.share.share_sheet.ShareSheetPropertyModelBuilder.ContentType;
+import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.browser_ui.share.ShareParams;
+import org.chromium.components.prefs.PrefService;
+import org.chromium.components.user_prefs.UserPrefs;
+import org.chromium.components.user_prefs.UserPrefsJni;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.test.util.DummyUiActivity;
 
@@ -55,8 +62,16 @@
     @Rule
     public TestRule mFeatureProcessor = new Features.JUnitProcessor();
 
+    @Rule
+    public JniMocker mJniMocker = new JniMocker();
+
     @Mock
-    private PrefServiceBridge mPrefServiceBridge;
+    private Profile.Natives mProfileNatives;
+    @Mock
+    private UserPrefs.Natives mUserPrefsNatives;
+
+    @Mock
+    private PrefService mPrefService;
 
     private static final String URL = "http://www.google.com/";
 
@@ -69,6 +84,10 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mJniMocker.mock(ProfileJni.TEST_HOOKS, mProfileNatives);
+        mJniMocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsNatives);
+        Mockito.when(mProfileNatives.fromWebContents(anyObject())).thenReturn(null);
+        Mockito.when(mUserPrefsNatives.get(anyObject())).thenReturn(mPrefService);
         mActivity = mActivityTestRule.getActivity();
     }
 
@@ -252,12 +271,15 @@
     }
 
     private void setUpChromeProvidedSharingOptionsProviderTest(boolean printingEnabled) {
-        Mockito.when(mPrefServiceBridge.getBoolean(anyString())).thenReturn(printingEnabled);
+        Mockito.when(mPrefService.getBoolean(anyString())).thenReturn(printingEnabled);
 
         mChromeProvidedSharingOptionsProvider = new ChromeProvidedSharingOptionsProvider(mActivity,
-                /*activityTabProvider=*/null, /*bottomSheetController=*/null,
+                /*activityTabProvider=*/
+                ()
+                        -> new MockTab(0, false),
+                /*bottomSheetController=*/null,
                 new ShareSheetBottomSheetContent(mActivity, mShareSheetCoordinator),
-                mPrefServiceBridge, new ShareParams.Builder(null, "", "").build(),
+                new ShareParams.Builder(null, "", "").build(),
                 new ChromeShareExtras.Builder().build(),
                 /*TabPrinterDelegate=*/null,
                 /*shareStartTime=*/0, mShareSheetCoordinator);
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
index 8439c6c..40a12041 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetCoordinatorTest.java
@@ -89,7 +89,7 @@
                 .thenReturn(thirdPartyPropertyModels);
 
         mShareSheetCoordinator =
-                new ShareSheetCoordinator(mController, null, mPropertyModelBuilder, null, null);
+                new ShareSheetCoordinator(mController, null, mPropertyModelBuilder, null);
     }
 
     @Test
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 4d87caac..84f6820d 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -1098,7 +1098,8 @@
 
 // Tests that when a subframe commits a main resource with a certificate error,
 // the navigation entry is marked as insecure.
-IN_PROC_BROWSER_TEST_F(SSLUITestIgnoreCertErrors, SubframeHasCertError) {
+// Flaky. See https://crbug.com/1106370.
+IN_PROC_BROWSER_TEST_F(SSLUITestIgnoreCertErrors, DISABLED_SubframeHasCertError) {
   ASSERT_TRUE(https_server_mismatched_.Start());
   // Load a page with a data: favicon URL to suppress a favicon request. A
   // favicon request can cause the navigation entry to get marked as having run
diff --git a/chrome/browser/task_manager/providers/arc/arc_process_task_provider.cc b/chrome/browser/task_manager/providers/arc/arc_process_task_provider.cc
index f8f22b1..0af2293 100644
--- a/chrome/browser/task_manager/providers/arc/arc_process_task_provider.cc
+++ b/chrome/browser/task_manager/providers/arc/arc_process_task_provider.cc
@@ -100,26 +100,30 @@
 void ArcProcessTaskProvider::RequestAppProcessList() {
   arc::ArcProcessService* arc_process_service =
       arc::ArcProcessService::Get();
-  auto callback = base::Bind(&ArcProcessTaskProvider::OnUpdateAppProcessList,
-                             weak_ptr_factory_.GetWeakPtr());
   if (!arc_process_service) {
     VLOG(2) << "ARC process instance is not ready.";
     ScheduleNextAppRequest();
     return;
   }
-  arc_process_service->RequestAppProcessList(callback);
+
+  auto callback =
+      base::BindOnce(&ArcProcessTaskProvider::OnUpdateAppProcessList,
+                     weak_ptr_factory_.GetWeakPtr());
+  arc_process_service->RequestAppProcessList(std::move(callback));
 }
 
 void ArcProcessTaskProvider::RequestSystemProcessList() {
   arc::ArcProcessService* arc_process_service = arc::ArcProcessService::Get();
-  auto callback = base::Bind(&ArcProcessTaskProvider::OnUpdateSystemProcessList,
-                             weak_ptr_factory_.GetWeakPtr());
   if (!arc_process_service) {
     VLOG(2) << "ARC process instance is not ready.";
     ScheduleNextSystemRequest();
     return;
   }
-  arc_process_service->RequestSystemProcessList(callback);
+
+  auto callback =
+      base::BindOnce(&ArcProcessTaskProvider::OnUpdateSystemProcessList,
+                     weak_ptr_factory_.GetWeakPtr());
+  arc_process_service->RequestSystemProcessList(std::move(callback));
 }
 
 void ArcProcessTaskProvider::StartUpdating() {
@@ -134,25 +138,27 @@
   nspid_to_sys_task_.clear();
 }
 
-void ArcProcessTaskProvider::ScheduleNextRequest(const base::Closure& task) {
+void ArcProcessTaskProvider::ScheduleNextRequest(base::OnceClosure task) {
   if (!is_updating_)
     return;
   // TODO(nya): Remove this timer once ARC starts to send us UpdateProcessList
   // message when the process list changed. As of today, ARC does not send
   // the process list unless we request it by RequestAppProcessList message.
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, task, arc::ArcProcessService::kProcessSnapshotRefreshTime);
+      FROM_HERE, std::move(task),
+      arc::ArcProcessService::kProcessSnapshotRefreshTime);
 }
 
 void ArcProcessTaskProvider::ScheduleNextAppRequest() {
-  ScheduleNextRequest(base::Bind(&ArcProcessTaskProvider::RequestAppProcessList,
-                                 weak_ptr_factory_.GetWeakPtr()));
+  ScheduleNextRequest(
+      base::BindOnce(&ArcProcessTaskProvider::RequestAppProcessList,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ArcProcessTaskProvider::ScheduleNextSystemRequest() {
   ScheduleNextRequest(
-      base::Bind(&ArcProcessTaskProvider::RequestSystemProcessList,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&ArcProcessTaskProvider::RequestSystemProcessList,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 }  // namespace task_manager
diff --git a/chrome/browser/task_manager/providers/arc/arc_process_task_provider.h b/chrome/browser/task_manager/providers/arc/arc_process_task_provider.h
index 5b642f76..a1a2fe1 100644
--- a/chrome/browser/task_manager/providers/arc/arc_process_task_provider.h
+++ b/chrome/browser/task_manager/providers/arc/arc_process_task_provider.h
@@ -42,7 +42,7 @@
   using ArcTaskMap =
       std::unordered_map<base::ProcessId, std::unique_ptr<ArcProcessTask>>;
   using OptionalArcProcessList = arc::ArcProcessService::OptionalArcProcessList;
-  void ScheduleNextRequest(const base::Closure& task);
+  void ScheduleNextRequest(base::OnceClosure task);
 
   // Auto-retry if ARC bridge service is not ready.
   void RequestAppProcessList();
diff --git a/chrome/browser/task_manager/providers/child_process_task.cc b/chrome/browser/task_manager/providers/child_process_task.cc
index 2878ffa0..bc107a4 100644
--- a/chrome/browser/task_manager/providers/child_process_task.cc
+++ b/chrome/browser/task_manager/providers/child_process_task.cc
@@ -194,7 +194,7 @@
   // invoke it and record the current values (which might be invalid at the
   // moment. We can safely ignore that and count on future refresh cycles
   // potentially having valid values).
-  process_resources_sampler_->Refresh(base::Closure());
+  process_resources_sampler_->Refresh(base::DoNothing());
 
   v8_memory_allocated_ = base::saturated_cast<int64_t>(
       process_resources_sampler_->GetV8MemoryAllocated());
diff --git a/chrome/browser/task_manager/providers/web_contents/renderer_task.cc b/chrome/browser/task_manager/providers/web_contents/renderer_task.cc
index 1fb42a1c..734cbe9 100644
--- a/chrome/browser/task_manager/providers/web_contents/renderer_task.cc
+++ b/chrome/browser/task_manager/providers/web_contents/renderer_task.cc
@@ -119,7 +119,7 @@
   // it and record the current values (which might be invalid at the moment. We
   // can safely ignore that and count on future refresh cycles potentially
   // having valid values).
-  renderer_resources_sampler_->Refresh(base::Closure());
+  renderer_resources_sampler_->Refresh(base::DoNothing());
 
   v8_memory_allocated_ = base::saturated_cast<int64_t>(
       renderer_resources_sampler_->GetV8MemoryAllocated());
diff --git a/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc b/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc
index 632543e..55995230 100644
--- a/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc
+++ b/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc
@@ -116,7 +116,7 @@
 
   favicon::ContentFaviconDriver* driver_;
   GURL target_favicon_url_;
-  base::Closure quit_closure_;
+  base::RepeatingClosure quit_closure_;
 
   DISALLOW_COPY_AND_ASSIGN(FaviconWaiter);
 };
diff --git a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
index d2024b8..8092f11 100644
--- a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
+++ b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
@@ -76,7 +76,7 @@
   void ClearTasksForDescendantsOf(RenderFrameHost* ancestor);
 
   // Calls |on_task| for each task managed by this WebContentsEntry.
-  void ForEachTask(const base::Callback<void(RendererTask*)>& on_task);
+  void ForEachTask(const base::RepeatingCallback<void(RendererTask*)> on_task);
 
   // Walks parents until hitting a process boundary. Returns the highest frame
   // in the same SiteInstance as |render_frame_host|.
@@ -121,7 +121,7 @@
 }
 
 void WebContentsEntry::ClearAllTasks(bool notify_observer) {
-  ForEachTask(base::Bind(
+  ForEachTask(base::BindRepeating(
       [](WebContentsTaskProvider* provider, bool notify_observer,
          content::WebContents* web_contents, RendererTask* task) {
         task->set_termination_status(web_contents->GetCrashedStatus());
@@ -240,7 +240,7 @@
   if (!main_frame_task)
     return;
 
-  ForEachTask(base::Bind([](RendererTask* task) {
+  ForEachTask(base::BindRepeating([](RendererTask* task) {
     // Listening to WebContentsObserver::TitleWasSet() only is not enough in
     // some cases when the the web page doesn't have a title. That's why we
     // update the title here as well.
@@ -255,7 +255,7 @@
 }
 
 void WebContentsEntry::TitleWasSet(content::NavigationEntry* entry) {
-  ForEachTask(base::Bind([](RendererTask* task) {
+  ForEachTask(base::BindRepeating([](RendererTask* task) {
     task->UpdateTitle();
     task->UpdateFavicon();
   }));
@@ -381,7 +381,7 @@
 }
 
 void WebContentsEntry::ForEachTask(
-    const base::Callback<void(RendererTask*)>& on_task) {
+    base::RepeatingCallback<void(RendererTask*)> on_task) {
   for (const auto& pair : frames_by_site_instance_) {
     const FramesList& frames_list = pair.second;
     DCHECK(!frames_list.empty());
diff --git a/chrome/browser/task_manager/sampling/shared_sampler.h b/chrome/browser/task_manager/sampling/shared_sampler.h
index 2849c6c13..f49d928 100644
--- a/chrome/browser/task_manager/sampling/shared_sampler.h
+++ b/chrome/browser/task_manager/sampling/shared_sampler.h
@@ -45,7 +45,7 @@
     base::Time start_time;
   };
   using OnSamplingCompleteCallback =
-      base::Callback<void(base::Optional<SamplingResult>)>;
+      base::RepeatingCallback<void(base::Optional<SamplingResult>)>;
 
   // Returns a combination of refresh flags supported by the shared sampler.
   int64_t GetSupportedFlags() const;
diff --git a/chrome/browser/task_manager/sampling/shared_sampler_win_unittest.cc b/chrome/browser/task_manager/sampling/shared_sampler_win_unittest.cc
index 529bcd31..caf1d11 100644
--- a/chrome/browser/task_manager/sampling/shared_sampler_win_unittest.cc
+++ b/chrome/browser/task_manager/sampling/shared_sampler_win_unittest.cc
@@ -36,8 +36,8 @@
         shared_sampler_(new SharedSampler(blocking_pool_runner_)) {
     shared_sampler_->RegisterCallback(
         base::GetCurrentProcId(),
-        base::Bind(&SharedSamplerTest::OnSamplerRefreshDone,
-                   base::Unretained(this)));
+        base::BindRepeating(&SharedSamplerTest::OnSamplerRefreshDone,
+                            base::Unretained(this)));
   }
 
   ~SharedSamplerTest() override {}
@@ -88,7 +88,7 @@
 
   int64_t expected_refresh_type_ = 0;
   int64_t finished_refresh_type_ = 0;
-  base::Closure quit_closure_;
+  base::RepeatingClosure quit_closure_;
 
   int idle_wakeups_per_second_ = -1;
   base::Time start_time_;
diff --git a/chrome/browser/task_manager/sampling/task_group.cc b/chrome/browser/task_manager/sampling/task_group.cc
index 0e855bf..5095006 100644
--- a/chrome/browser/task_manager/sampling/task_group.cc
+++ b/chrome/browser/task_manager/sampling/task_group.cc
@@ -83,7 +83,7 @@
     base::ProcessHandle proc_handle,
     base::ProcessId proc_id,
     bool is_running_in_vm,
-    const base::Closure& on_background_calculations_done,
+    const base::RepeatingClosure& on_background_calculations_done,
     const scoped_refptr<SharedSampler>& shared_sampler,
     const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner)
     : process_handle_(proc_handle),
@@ -122,22 +122,22 @@
   if (process_id_ != base::kNullProcessId && !is_running_in_vm_) {
     worker_thread_sampler_ = base::MakeRefCounted<TaskGroupSampler>(
         base::Process::Open(process_id_), blocking_pool_runner,
-        base::Bind(&TaskGroup::OnCpuRefreshDone,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&TaskGroup::OnSwappedMemRefreshDone,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&TaskGroup::OnIdleWakeupsRefreshDone,
-                   weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(&TaskGroup::OnCpuRefreshDone,
+                            weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(&TaskGroup::OnSwappedMemRefreshDone,
+                            weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(&TaskGroup::OnIdleWakeupsRefreshDone,
+                            weak_ptr_factory_.GetWeakPtr()),
 #if defined(OS_LINUX) || defined(OS_MACOSX)
-        base::Bind(&TaskGroup::OnOpenFdCountRefreshDone,
-                   weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(&TaskGroup::OnOpenFdCountRefreshDone,
+                            weak_ptr_factory_.GetWeakPtr()),
 #endif  // defined(OS_LINUX) || defined(OS_MACOSX)
-        base::Bind(&TaskGroup::OnProcessPriorityDone,
-                   weak_ptr_factory_.GetWeakPtr()));
+        base::BindRepeating(&TaskGroup::OnProcessPriorityDone,
+                            weak_ptr_factory_.GetWeakPtr()));
 
     shared_sampler_->RegisterCallback(
-        process_id_,
-        base::Bind(&TaskGroup::OnSamplerRefreshDone, base::Unretained(this)));
+        process_id_, base::BindRepeating(&TaskGroup::OnSamplerRefreshDone,
+                                         base::Unretained(this)));
   }
 }
 
diff --git a/chrome/browser/task_manager/sampling/task_group.h b/chrome/browser/task_manager/sampling/task_group.h
index ec99734..4867506 100644
--- a/chrome/browser/task_manager/sampling/task_group.h
+++ b/chrome/browser/task_manager/sampling/task_group.h
@@ -54,7 +54,7 @@
       base::ProcessHandle proc_handle,
       base::ProcessId proc_id,
       bool is_running_in_vm,
-      const base::Closure& on_background_calculations_done,
+      const base::RepeatingClosure& on_background_calculations_done,
       const scoped_refptr<SharedSampler>& shared_sampler,
       const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner);
   ~TaskGroup();
@@ -164,7 +164,7 @@
 
   // This is a callback into the TaskManagerImpl to inform it that the
   // background calculations for this TaskGroup has finished.
-  const base::Closure on_background_calculations_done_;
+  const base::RepeatingClosure on_background_calculations_done_;
 
   scoped_refptr<TaskGroupSampler> worker_thread_sampler_;
 
diff --git a/chrome/browser/task_manager/sampling/task_group_sampler.h b/chrome/browser/task_manager/sampling/task_group_sampler.h
index d990ac9..661596a 100644
--- a/chrome/browser/task_manager/sampling/task_group_sampler.h
+++ b/chrome/browser/task_manager/sampling/task_group_sampler.h
@@ -29,13 +29,13 @@
  public:
   // Below are the types of callbacks that are invoked on the UI thread upon
   // completion of corresponding refresh tasks on the worker thread.
-  using OnCpuRefreshCallback = base::Callback<void(double)>;
-  using OnSwappedMemRefreshCallback = base::Callback<void(int64_t)>;
-  using OnIdleWakeupsCallback = base::Callback<void(int)>;
+  using OnCpuRefreshCallback = base::RepeatingCallback<void(double)>;
+  using OnSwappedMemRefreshCallback = base::RepeatingCallback<void(int64_t)>;
+  using OnIdleWakeupsCallback = base::RepeatingCallback<void(int)>;
 #if defined(OS_LINUX) || defined(OS_MACOSX)
-  using OnOpenFdCountCallback = base::Callback<void(int)>;
+  using OnOpenFdCountCallback = base::RepeatingCallback<void(int)>;
 #endif  // defined(OS_LINUX) || defined(OS_MACOSX)
-  using OnProcessPriorityCallback = base::Callback<void(bool)>;
+  using OnProcessPriorityCallback = base::RepeatingCallback<void(bool)>;
 
   TaskGroupSampler(
       base::Process process,
diff --git a/chrome/browser/task_manager/sampling/task_group_unittest.cc b/chrome/browser/task_manager/sampling/task_group_unittest.cc
index ea31b166..1339aa0 100644
--- a/chrome/browser/task_manager/sampling/task_group_unittest.cc
+++ b/chrome/browser/task_manager/sampling/task_group_unittest.cc
@@ -71,8 +71,8 @@
     task_group_ = std::make_unique<TaskGroup>(
         base::Process::Current().Handle(), base::Process::Current().Pid(),
         is_running_in_vm,
-        base::Bind(&TaskGroupTest::OnBackgroundCalculationsDone,
-                   base::Unretained(this)),
+        base::BindRepeating(&TaskGroupTest::OnBackgroundCalculationsDone,
+                            base::Unretained(this)),
         new SharedSampler(io_task_runner_), io_task_runner_);
     // Refresh() is only valid on non-empty TaskGroups, so add a fake Task.
     fake_task_ = std::make_unique<FakeTask>(base::Process::Current().Pid(),
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl.cc b/chrome/browser/task_manager/sampling/task_manager_impl.cc
index 0b34e84..bd3ac14 100644
--- a/chrome/browser/task_manager/sampling/task_manager_impl.cc
+++ b/chrome/browser/task_manager/sampling/task_manager_impl.cc
@@ -74,9 +74,9 @@
 }
 
 TaskManagerImpl::TaskManagerImpl()
-    : on_background_data_ready_callback_(
-          base::Bind(&TaskManagerImpl::OnTaskGroupBackgroundCalculationsDone,
-                     base::Unretained(this))),
+    : on_background_data_ready_callback_(base::BindRepeating(
+          &TaskManagerImpl::OnTaskGroupBackgroundCalculationsDone,
+          base::Unretained(this))),
       blocking_pool_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
            base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
@@ -602,8 +602,8 @@
       !waiting_for_memory_dump_) {
     // The callback keeps this object alive until the callback is invoked.
     waiting_for_memory_dump_ = true;
-    auto callback = base::Bind(&TaskManagerImpl::OnReceivedMemoryDump,
-                               weak_ptr_factory_.GetWeakPtr());
+    auto callback = base::BindOnce(&TaskManagerImpl::OnReceivedMemoryDump,
+                                   weak_ptr_factory_.GetWeakPtr());
     memory_instrumentation::MemoryInstrumentation::GetInstance()
         ->RequestPrivateMemoryFootprint(base::kNullProcessId,
                                         std::move(callback));
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl.h b/chrome/browser/task_manager/sampling/task_manager_impl.h
index 1b314e2..4a329559 100644
--- a/chrome/browser/task_manager/sampling/task_manager_impl.h
+++ b/chrome/browser/task_manager/sampling/task_manager_impl.h
@@ -173,7 +173,7 @@
   // background thread has completed.
   void OnTaskGroupBackgroundCalculationsDone();
 
-  const base::Closure on_background_data_ready_callback_;
+  const base::RepeatingClosure on_background_data_ready_callback_;
 
   // Map TaskGroups by the IDs of the processes they represent.
   PidToTaskGroupMap task_groups_by_proc_id_;
diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc
index 57ea9e7..be8e2d53 100644
--- a/chrome/browser/task_manager/task_manager_browsertest.cc
+++ b/chrome/browser/task_manager/task_manager_browsertest.cc
@@ -92,9 +92,9 @@
     // Show the task manager. This populates the model, and helps with debugging
     // (you see the task manager).
     chrome::ShowTaskManager(browser());
-    model_ = task_manager::TaskManagerTester::Create(
-        base::Bind(&TaskManagerBrowserTest::TaskManagerTableModelSanityCheck,
-                   base::Unretained(this)));
+    model_ = task_manager::TaskManagerTester::Create(base::BindRepeating(
+        &TaskManagerBrowserTest::TaskManagerTableModelSanityCheck,
+        base::Unretained(this)));
   }
 
   void HideTaskManager() {
diff --git a/chrome/browser/task_manager/task_manager_browsertest_util.cc b/chrome/browser/task_manager/task_manager_browsertest_util.cc
index d23e53df..54029b1 100644
--- a/chrome/browser/task_manager/task_manager_browsertest_util.cc
+++ b/chrome/browser/task_manager/task_manager_browsertest_util.cc
@@ -44,7 +44,7 @@
         title_pattern_(title_pattern),
         column_specifier_(column_specifier),
         min_column_value_(min_column_value) {
-    task_manager_tester_ = TaskManagerTester::Create(base::Bind(
+    task_manager_tester_ = TaskManagerTester::Create(base::BindRepeating(
         &ResourceChangeObserver::OnResourceChange, base::Unretained(this)));
   }
 
diff --git a/chrome/browser/task_manager/task_manager_interface.cc b/chrome/browser/task_manager/task_manager_interface.cc
index 3357a9d..2c91c6a7 100644
--- a/chrome/browser/task_manager/task_manager_interface.cc
+++ b/chrome/browser/task_manager/task_manager_interface.cc
@@ -146,10 +146,9 @@
 }
 
 void TaskManagerInterface::ScheduleRefresh(base::TimeDelta refresh_time) {
-  refresh_timer_->Start(FROM_HERE,
-                        refresh_time,
-                        base::Bind(&TaskManagerInterface::Refresh,
-                                   base::Unretained(this)));
+  refresh_timer_->Start(FROM_HERE, refresh_time,
+                        base::BindRepeating(&TaskManagerInterface::Refresh,
+                                            base::Unretained(this)));
 }
 
 }  // namespace task_manager
diff --git a/chrome/browser/task_manager/task_manager_tester.cc b/chrome/browser/task_manager/task_manager_tester.cc
index 2a2fd93..60cad30 100644
--- a/chrome/browser/task_manager/task_manager_tester.cc
+++ b/chrome/browser/task_manager/task_manager_tester.cc
@@ -24,7 +24,7 @@
   ScopedInterceptTableModelObserver(
       ui::TableModel* model_to_intercept,
       ui::TableModelObserver* real_table_model_observer,
-      const base::Closure& callback)
+      const base::RepeatingClosure& callback)
       : model_to_intercept_(model_to_intercept),
         real_table_model_observer_(real_table_model_observer),
         callback_(callback) {
@@ -56,7 +56,7 @@
  private:
   ui::TableModel* model_to_intercept_;
   ui::TableModelObserver* real_table_model_observer_;
-  base::Closure callback_;
+  base::RepeatingClosure callback_;
 };
 
 namespace {
@@ -68,7 +68,8 @@
 
 }  // namespace
 
-TaskManagerTester::TaskManagerTester(const base::Closure& on_resource_change)
+TaskManagerTester::TaskManagerTester(
+    const base::RepeatingClosure& on_resource_change)
     : model_(GetRealModel()) {
   // Eavesdrop the model->view conversation, since the model only supports
   // single observation.
@@ -188,7 +189,7 @@
 
 // static
 std::unique_ptr<TaskManagerTester> TaskManagerTester::Create(
-    const base::Closure& callback) {
+    const base::RepeatingClosure& callback) {
   return base::WrapUnique(new TaskManagerTester(callback));
 }
 
diff --git a/chrome/browser/task_manager/task_manager_tester.h b/chrome/browser/task_manager/task_manager_tester.h
index dff4e5a9..9624811 100644
--- a/chrome/browser/task_manager/task_manager_tester.h
+++ b/chrome/browser/task_manager/task_manager_tester.h
@@ -32,7 +32,7 @@
   // |on_resource_change|, if not a null callback, will be invoked when the
   // underlying model changes.
   static std::unique_ptr<TaskManagerTester> Create(
-      const base::Closure& on_resource_change);
+      const base::RepeatingClosure& on_resource_change);
 
   // Get the number of rows currently in the task manager.
   int GetRowCount();
@@ -59,7 +59,7 @@
   void GetRowsGroupRange(int row, int* out_start, int* out_length);
 
  private:
-  explicit TaskManagerTester(const base::Closure& on_resource_change);
+  explicit TaskManagerTester(const base::RepeatingClosure& on_resource_change);
 
   TaskManagerInterface* task_manager();
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 377211c47..1fa7a23b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1808,6 +1808,8 @@
       "ash/chrome_screenshot_grabber_test_observer.h",
       "ash/chrome_shell_delegate.cc",
       "ash/chrome_shell_delegate.h",
+      "ash/clipboard_util.cc",
+      "ash/clipboard_util.h",
       "ash/image_downloader_impl.cc",
       "ash/image_downloader_impl.h",
       "ash/ime_controller_client.cc",
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
index e2ecf66d..177b334 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
@@ -335,6 +335,12 @@
     }
 
     boolean shouldShowAppMenu() {
+        // If the activity's decor view is not attached to window, we don't show the app menu
+        // because the window manager might have revoked the window token for this activity. See
+        // https://crbug.com/1105831.
+        if (!mDecorView.isAttachedToWindow()) {
+            return false;
+        }
         for (int i = 0; i < mBlockers.size(); i++) {
             if (!mBlockers.get(i).canShowAppMenu()) return false;
         }
diff --git a/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc b/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc
index 3027236..c1bc865c 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_context_menu.cc
@@ -243,15 +243,23 @@
     index = 1;
   }
 
+  // The special rule to ensure that FilesManager's first menu item is "New
+  // window".
+  const bool build_extension_menu_before_default =
+      (app_type_ == apps::mojom::AppType::kExtension &&
+       app_id() == extension_misc::kFilesManagerAppId);
+
+  if (build_extension_menu_before_default)
+    BuildExtensionAppShortcutsMenu(menu_model.get());
+
   // Create default items.
   if (app_id() != extension_misc::kChromeAppId &&
       app_type_ != apps::mojom::AppType::kUnknown) {
     app_list::AppContextMenu::BuildMenu(menu_model.get());
   }
 
-  if (app_type_ == apps::mojom::AppType::kExtension) {
+  if (!build_extension_menu_before_default)
     BuildExtensionAppShortcutsMenu(menu_model.get());
-  }
 
   app_shortcut_items_ = std::make_unique<arc::ArcAppShortcutItems>();
   for (size_t i = index; i < menu_items->items.size(); i++) {
diff --git a/chrome/browser/ui/ash/chrome_screenshot_grabber.cc b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
index f34d189..11d03ccd 100644
--- a/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
+++ b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
@@ -36,6 +36,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/chrome_screenshot_grabber_test_observer.h"
+#include "chrome/browser/ui/ash/clipboard_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
@@ -44,8 +45,6 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/data_decoder/public/cpp/decode_image.h"
-#include "ui/base/clipboard/clipboard.h"
-#include "ui/base/clipboard/scoped_clipboard_writer.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -59,9 +58,6 @@
 
 const char kNotificationOriginUrl[] = "chrome://screenshot";
 
-const char kImageClipboardFormatPrefix[] = "<img src='data:image/png;base64,";
-const char kImageClipboardFormatSuffix[] = "'>";
-
 // User is waiting for the screenshot-taken notification, hence USER_VISIBLE.
 constexpr base::TaskTraits kBlockingTaskTraits = {
     base::MayBlock(), base::TaskPriority::USER_VISIBLE,
@@ -69,53 +65,6 @@
 
 ChromeScreenshotGrabber* g_chrome_screenshot_grabber_instance = nullptr;
 
-void CopyScreenshotToClipboard(scoped_refptr<base::RefCountedString> png_data,
-                               const SkBitmap& decoded_image) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  std::string encoded;
-  base::Base64Encode(png_data->data(), &encoded);
-  {
-    ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
-
-    // Send both HTML and and Image formats to clipboard. HTML format is needed
-    // by ARC, while Image is needed by Hangout.
-    std::string html(kImageClipboardFormatPrefix);
-    html += encoded;
-    html += kImageClipboardFormatSuffix;
-    scw.WriteHTML(base::UTF8ToUTF16(html), std::string());
-    scw.WriteImage(decoded_image);
-  }
-  base::RecordAction(base::UserMetricsAction("Screenshot_CopyClipboard"));
-}
-
-void DecodeFileAndCopyToClipboard(
-    scoped_refptr<base::RefCountedString> png_data) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  // Decode the image in sandboxed process because |png_data| comes from
-  // external storage.
-  data_decoder::DecodeImageIsolated(
-      std::vector<uint8_t>(png_data->data().begin(), png_data->data().end()),
-      data_decoder::mojom::ImageCodec::DEFAULT, false,
-      data_decoder::kDefaultMaxSizeInBytes, gfx::Size(),
-      base::BindOnce(&CopyScreenshotToClipboard, png_data));
-}
-
-void ReadFileAndCopyToClipboardLocal(const base::FilePath& screenshot_path) {
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::WILL_BLOCK);
-  scoped_refptr<base::RefCountedString> png_data(new base::RefCountedString());
-  if (!base::ReadFileToString(screenshot_path, &(png_data->data()))) {
-    LOG(ERROR) << "Failed to read the screenshot file: "
-               << screenshot_path.value();
-    return;
-  }
-
-  content::GetUIThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(&DecodeFileAndCopyToClipboard, png_data));
-}
-
 // Delegate for a notification. This class has two roles: to implement callback
 // methods for notification, and to provide an identity of the associated
 // notification.
@@ -151,7 +100,9 @@
         // screenshot file and copy it to the clipboard.
         base::ThreadPool::PostTask(
             FROM_HERE, kBlockingTaskTraits,
-            base::BindOnce(&ReadFileAndCopyToClipboardLocal, screenshot_path_));
+            base::BindOnce(&clipboard_util::ReadFileAndCopyToClipboardLocal,
+                           screenshot_path_));
+        base::RecordAction(base::UserMetricsAction("Screenshot_CopyClipboard"));
         break;
       }
       case BUTTON_ANNOTATE: {
diff --git a/chrome/browser/ui/ash/clipboard_util.cc b/chrome/browser/ui/ash/clipboard_util.cc
new file mode 100644
index 0000000..4380434
--- /dev/null
+++ b/chrome/browser/ui/ash/clipboard_util.cc
@@ -0,0 +1,71 @@
+// 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/ui/ash/clipboard_util.h"
+
+#include "base/base64.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/metrics/user_metrics.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "services/data_decoder/public/cpp/decode_image.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/scoped_clipboard_writer.h"
+
+namespace clipboard_util {
+namespace {
+
+const char kImageClipboardFormatPrefix[] = "<img src='data:image/png;base64,";
+const char kImageClipboardFormatSuffix[] = "'>";
+
+void CopyImageToClipboard(scoped_refptr<base::RefCountedString> png_data,
+                          const SkBitmap& decoded_image) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  std::string encoded;
+  base::Base64Encode(png_data->data(), &encoded);
+  {
+    ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
+
+    // Send both HTML and and Image formats to clipboard. HTML format is needed
+    // by ARC, while Image is needed by Hangout.
+    std::string html(kImageClipboardFormatPrefix);
+    html += encoded;
+    html += kImageClipboardFormatSuffix;
+    scw.WriteHTML(base::UTF8ToUTF16(html), std::string());
+    scw.WriteImage(decoded_image);
+  }
+}
+
+void DecodeFileAndCopyToClipboard(
+    scoped_refptr<base::RefCountedString> png_data) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // Decode the image in sandboxed process because |png_data| comes from
+  // external storage.
+  data_decoder::DecodeImageIsolated(
+      std::vector<uint8_t>(png_data->data().begin(), png_data->data().end()),
+      data_decoder::mojom::ImageCodec::DEFAULT, false,
+      data_decoder::kDefaultMaxSizeInBytes, gfx::Size(),
+      base::BindOnce(&CopyImageToClipboard, png_data));
+}
+
+}  // namespace
+
+void ReadFileAndCopyToClipboardLocal(const base::FilePath& local_file) {
+  DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::WILL_BLOCK);
+  scoped_refptr<base::RefCountedString> png_data(new base::RefCountedString());
+  if (!base::ReadFileToString(local_file, &(png_data->data()))) {
+    LOG(ERROR) << "Failed to read the screenshot file: " << local_file.value();
+    return;
+  }
+
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(&DecodeFileAndCopyToClipboard, png_data));
+}
+}  // namespace clipboard_util
diff --git a/chrome/browser/ui/ash/clipboard_util.h b/chrome/browser/ui/ash/clipboard_util.h
new file mode 100644
index 0000000..ecaf89e
--- /dev/null
+++ b/chrome/browser/ui/ash/clipboard_util.h
@@ -0,0 +1,20 @@
+// 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_UI_ASH_CLIPBOARD_UTIL_H_
+#define CHROME_BROWSER_UI_ASH_CLIPBOARD_UTIL_H_
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace clipboard_util {
+
+// Reads a local file and then copies that file to the system clipboard. This
+// should not be run on the UI Thread as it performs blocking IO.
+void ReadFileAndCopyToClipboardLocal(const base::FilePath& local_file);
+
+}  // namespace clipboard_util
+
+#endif  // CHROME_BROWSER_UI_ASH_CLIPBOARD_UTIL_H_
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.cc b/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.cc
index 4325349..a69770b 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.cc
@@ -279,6 +279,15 @@
     index = 1;
   }
 
+  // The special rule to ensure that FilesManager's first menu item is "New
+  // window".
+  const bool build_extension_menu_before_pin =
+      (app_type_ == apps::mojom::AppType::kExtension &&
+       item().id.app_id == extension_misc::kFilesManagerAppId);
+
+  if (build_extension_menu_before_pin)
+    BuildExtensionAppShortcutsMenu(menu_model.get());
+
   if (ShouldAddPinMenu())
     AddPinMenu(menu_model.get());
 
@@ -310,7 +319,7 @@
     return;
   }
 
-  if (app_type_ == apps::mojom::AppType::kExtension)
+  if (!build_extension_menu_before_pin)
     BuildExtensionAppShortcutsMenu(menu_model.get());
 
   // When Crostini generates shelf id with the prefix "crostini:", AppService
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index d21fb2e..04b6acb 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -47,12 +47,14 @@
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/accessibility/speech_monitor.h"
+#include "chrome/browser/chromeos/file_manager/file_manager_test_util.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/launch_util.h"
+#include "chrome/browser/extensions/menu_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
@@ -101,6 +103,7 @@
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "extensions/browser/app_window/native_app_window.h"
 #include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry_factory.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/switches.h"
@@ -1287,6 +1290,68 @@
   EXPECT_EQ(++tab_count, tab_strip->count());
 }
 
+// The Browsertest verifying FilesManager's features.
+class FilesManagerExtensionTest : public LauncherPlatformAppBrowserTest {
+ public:
+  void SetUpOnMainThread() override {
+    LauncherPlatformAppBrowserTest::SetUpOnMainThread();
+    CHECK(profile());
+
+    file_manager::test::AddDefaultComponentExtensionsOnMainThread(profile());
+  }
+};
+
+// Verifies that FilesManager's first shelf context menu item is "New window"
+// (see https://crbug.com/1102781).
+IN_PROC_BROWSER_TEST_F(FilesManagerExtensionTest, VerifyFirstItem) {
+  const auto* extension =
+      extensions::ExtensionRegistryFactory::GetForBrowserContext(profile())
+          ->GetExtensionById(extension_misc::kFilesManagerAppId,
+                             extensions::ExtensionRegistry::ENABLED);
+  EXPECT_TRUE(extension);
+
+  // Hacky way to configure FileManager's "New window" menu option.
+  const std::string top_level_item_label("New window");
+  {
+    extensions::MenuItem::Type type = extensions::MenuItem::NORMAL;
+
+    // |contexts| must contain MenuItem::LAUNCHER. Otherwise the menu item will
+    // be ignored by AppServiceShelfContextMenu.
+    extensions::MenuItem::ContextList contexts(extensions::MenuItem::LAUNCHER);
+
+    extensions::MenuItem::Id id(
+        /*incognite=*/false,
+        extensions::MenuItem::ExtensionKey(extension->id()));
+    std::unique_ptr<extensions::MenuItem> top_item =
+        std::make_unique<extensions::MenuItem>(
+            id, top_level_item_label, /*checked=*/false, /*visible=*/true,
+            /*enabled=*/true, type, contexts);
+    extensions::MenuManager::Get(profile())->AddContextItem(
+        extension, std::move(top_item));
+    apps::AppServiceProxyFactory::GetForProfile(profile())
+        ->FlushMojoCallsForTesting();
+  }
+
+  CreateAppShortcutLauncherItem(ash::ShelfID(extension->id()));
+
+  const int item_count = shelf_model()->item_count();
+  ash::ShelfItem item = shelf_model()->items()[item_count - 1];
+  int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
+  auto menu = ShelfContextMenu::Create(controller_, &item, display_id);
+
+  // Fetch |extension|'s shelf context menu model and verify that the top level
+  // menu item should be the first one.
+  base::RunLoop run_loop;
+  menu->GetMenuModel(base::BindLambdaForTesting(
+      [&](std::unique_ptr<ui::SimpleMenuModel> menu_model) {
+        EXPECT_EQ(base::ASCIIToUTF16(top_level_item_label),
+                  menu_model->GetLabelAt(0));
+        run_loop.Quit();
+      }));
+
+  run_loop.Run();
+}
+
 // Launching an app from the shelf when not in Demo Mode should not record app
 // launch stat.
 IN_PROC_BROWSER_TEST_F(ShelfAppBrowserTest, NoDemoModeAppLaunchSourceReported) {
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 7edf844..be3fc3d8 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -218,6 +218,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/buildflags/buildflags.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
diff --git a/chrome/browser/ui/browser_list.h b/chrome/browser/ui/browser_list.h
index d6240a25..0f8b251 100644
--- a/chrome/browser/ui/browser_list.h
+++ b/chrome/browser/ui/browser_list.h
@@ -29,7 +29,7 @@
  public:
   using BrowserSet = base::flat_set<Browser*>;
   using BrowserVector = std::vector<Browser*>;
-  using CloseCallback = base::Callback<void(const base::FilePath&)>;
+  using CloseCallback = base::RepeatingCallback<void(const base::FilePath&)>;
   using const_iterator = BrowserVector::const_iterator;
   using const_reverse_iterator = BrowserVector::const_reverse_iterator;
 
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index 023a2ec3..0945d11 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -66,6 +66,7 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble.cc b/chrome/browser/ui/views/accessibility/caption_bubble.cc
index 8d9e041c..686f9bc4 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble.cc
+++ b/chrome/browser/ui/views/accessibility/caption_bubble.cc
@@ -13,6 +13,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string16.h"
 #include "build/build_config.h"
+#include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/accessibility/caption_controller.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/grit/generated_resources.h"
@@ -43,18 +44,18 @@
 #include "ui/views/view_class_properties.h"
 
 namespace {
+
 // Formatting constants
 static constexpr int kLineHeightDip = 24;
-static constexpr int kNumLines = 2;
+static constexpr int kNumLinesCollapsed = 2;
+static constexpr int kNumLinesExpanded = 8;
 static constexpr int kCornerRadiusDip = 4;
-static constexpr int kBottomPaddingDip = 28;
-static constexpr int kSidePaddingDip = 24;
-static constexpr int kCloseButtonDip = 16;
-static constexpr int kCloseButtonCircleHighlightPaddingDip = 2;
-static constexpr int kCloseButtonMarginDip = 6;
+static constexpr int kSidePaddingDip = 18;
+static constexpr int kButtonDip = 16;
+static constexpr int kButtonCircleHighlightPaddingDip = 2;
 // The preferred width of the bubble within its anchor.
 static constexpr double kPreferredAnchorWidthPercentage = 0.8;
-static constexpr int kMaxWidthDip = 548;
+static constexpr int kMaxWidthDip = 536;
 // Margin of the bubble with respect to the anchor window.
 static constexpr int kMinAnchorMarginDip = 20;
 static constexpr int kCaptionBubbleAlpha = 230;  // 90% opacity
@@ -77,9 +78,13 @@
 // Caption Bubble is focused.
 class CaptionBubbleFrameView : public views::BubbleFrameView {
  public:
-  explicit CaptionBubbleFrameView(views::View* close_button)
+  explicit CaptionBubbleFrameView(views::View* close_button,
+                                  views::View* expand_button,
+                                  views::View* collapse_button)
       : views::BubbleFrameView(gfx::Insets(), gfx::Insets()),
-        close_button_(close_button) {
+        close_button_(close_button),
+        expand_button_(expand_button),
+        collapse_button_(collapse_button) {
     // The focus ring is drawn on CaptionBubbleFrameView because it has the
     // correct bounds, but focused state is taken from the CaptionBubble.
     focus_ring_ = views::FocusRing::Install(this);
@@ -126,11 +131,15 @@
 
     // |point| is in coordinates relative to CaptionBubbleFrameView, i.e.
     // (0,0) is the upper left corner of this view. Convert it to screen
-    // coordinates to see whether the close button contains this point.
+    // coordinates to see whether one of the buttons contains this point.
+    // If it is, return HTCLIENT, so that the click is sent through to be
+    // handled by CaptionBubble::BubblePressed().
     gfx::Point point_in_screen =
         GetBoundsInScreen().origin() + gfx::Vector2d(point.x(), point.y());
-    if (close_button_->GetBoundsInScreen().Contains(point_in_screen))
-      return HTCLOSE;
+    if (close_button_->GetBoundsInScreen().Contains(point_in_screen) ||
+        expand_button_->GetBoundsInScreen().Contains(point_in_screen) ||
+        collapse_button_->GetBoundsInScreen().Contains(point_in_screen))
+      return HTCLIENT;
 
     // Ensure it's within the BubbleFrameView. This takes into account the
     // rounded corners and drop shadow of the BubbleBorder.
@@ -153,6 +162,8 @@
 
  private:
   views::View* close_button_;
+  views::View* expand_button_;
+  views::View* collapse_button_;
   views::FocusRing* focus_ring_ = nullptr;
   bool contents_focused_ = false;
 };
@@ -273,29 +284,22 @@
 }
 
 void CaptionBubble::Init() {
-  int content_bottom_margin = kBottomPaddingDip - kCloseButtonMarginDip;
-  int content_side_margin = kSidePaddingDip - kCloseButtonMarginDip;
-
   views::View* content_container = new views::View();
-  views::FlexLayout* layout = content_container->SetLayoutManager(
-      std::make_unique<views::FlexLayout>());
-  layout->SetOrientation(views::LayoutOrientation::kVertical);
-  layout->SetMainAxisAlignment(views::LayoutAlignment::kEnd);
-  layout->SetInteriorMargin(gfx::Insets(
-      0, content_side_margin, content_bottom_margin, content_side_margin));
-  layout->SetDefault(
-      views::kFlexBehaviorKey,
-      views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
-                               views::MaximumFlexSizeRule::kPreferred,
-                               /*adjust_height_for_width*/ true));
+  content_container->SetLayoutManager(std::make_unique<views::FlexLayout>())
+      ->SetOrientation(views::LayoutOrientation::kVertical)
+      .SetMainAxisAlignment(views::LayoutAlignment::kEnd)
+      .SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
+      .SetInteriorMargin(gfx::Insets(0, kSidePaddingDip))
+      .SetDefault(
+          views::kFlexBehaviorKey,
+          views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
+                                   views::MaximumFlexSizeRule::kPreferred,
+                                   /*adjust_height_for_width*/ true));
 
-  views::BoxLayout* main_layout =
-      SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kVertical,
-          gfx::Insets(kCloseButtonMarginDip), 0));
-  main_layout->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kEnd);
-  set_margins(gfx::Insets());
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+                       views::BoxLayout::Orientation::kVertical))
+      ->set_cross_axis_alignment(views::BoxLayout::CrossAxisAlignment::kEnd);
+  UseCompactMargins();
 
   // TODO(crbug.com/1055150): Use system caption color scheme rather than
   // hard-coding the colors.
@@ -306,12 +310,12 @@
 
   auto label = std::make_unique<views::Label>();
   label->SetMultiLine(true);
-  label->SetMaximumWidth(kMaxWidthDip - content_side_margin * 2);
+  label->SetMaximumWidth(kMaxWidthDip - kSidePaddingDip * 2);
   label->SetEnabledColor(SK_ColorWHITE);
   label->SetBackgroundColor(SK_ColorTRANSPARENT);
   label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
+  label->SetVerticalAlignment(gfx::VerticalAlignment::ALIGN_TOP);
   label->SetTooltipText(base::string16());
-
   // Render text truncates the end of text that is greater than 10000 chars.
   // While it is unlikely that the text will exceed 10000 chars, it is not
   // impossible, if the speech service sends a very long transcription_result.
@@ -338,25 +342,23 @@
       gfx::CreateVectorIcon(vector_icons::kErrorOutlineIcon, SK_ColorWHITE));
 
   auto error_message = std::make_unique<views::View>();
-  views::BoxLayout* error_layout =
-      error_message->SetLayoutManager(std::make_unique<views::BoxLayout>(
+  error_message
+      ->SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
-          kErrorMessageBetweenChildSpacingDip));
-  error_layout->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kCenter);
+          kErrorMessageBetweenChildSpacingDip))
+      ->set_cross_axis_alignment(views::BoxLayout::CrossAxisAlignment::kCenter);
   error_message->SetVisible(false);
 
-  auto close_button = views::CreateVectorImageButton(this);
-  views::SetImageFromVectorIcon(close_button.get(),
-                                vector_icons::kCloseRoundedIcon,
-                                kCloseButtonDip, SK_ColorWHITE);
-  close_button->SetTooltipText(
-      l10n_util::GetStringUTF16(IDS_LIVE_CAPTION_BUBBLE_CLOSE));
-  close_button->set_ink_drop_base_color(SkColor(gfx::kGoogleGrey600));
-  close_button->SizeToPreferredSize();
-  close_button->SetFocusForPlatform();
-  views::InstallCircleHighlightPathGenerator(
-      close_button.get(), gfx::Insets(kCloseButtonCircleHighlightPaddingDip));
+  auto expand_button =
+      BuildImageButton(kCaretDownIcon, IDS_LIVE_CAPTION_BUBBLE_EXPAND);
+  expand_button->SetVisible(!is_expanded_);
+
+  auto collapse_button =
+      BuildImageButton(kCaretUpIcon, IDS_LIVE_CAPTION_BUBBLE_COLLAPSE);
+  collapse_button->SetVisible(is_expanded_);
+
+  auto close_button = BuildImageButton(vector_icons::kCloseRoundedIcon,
+                                       IDS_LIVE_CAPTION_BUBBLE_CLOSE);
 
   wait_text_ = content_container->AddChildView(std::move(wait_text));
   label_ = content_container->AddChildView(std::move(label));
@@ -365,21 +367,41 @@
   error_text_ = error_message->AddChildView(std::move(error_text));
   error_message_ = content_container->AddChildView(std::move(error_message));
 
+  expand_button_ = content_container->AddChildView(std::move(expand_button));
+  collapse_button_ =
+      content_container->AddChildView(std::move(collapse_button));
+
   close_button_ = AddChildView(std::move(close_button));
-  content_container_ = AddChildView(content_container);
+  content_container_ = AddChildView(std::move(content_container));
 
   UpdateTextSize();
+  UpdateContentSize();
+}
+
+std::unique_ptr<views::ImageButton> CaptionBubble::BuildImageButton(
+    const gfx::VectorIcon& icon,
+    const int tooltip_text_id) {
+  auto button = views::CreateVectorImageButton(this);
+  views::SetImageFromVectorIcon(button.get(), icon, kButtonDip, SK_ColorWHITE);
+  button->SetTooltipText(l10n_util::GetStringUTF16(tooltip_text_id));
+  button->set_ink_drop_base_color(SkColor(gfx::kGoogleGrey600));
+  button->SizeToPreferredSize();
+  button->SetFocusForPlatform();
+  views::InstallCircleHighlightPathGenerator(
+      button.get(), gfx::Insets(kButtonCircleHighlightPaddingDip));
+  return button;
 }
 
 bool CaptionBubble::ShouldShowCloseButton() const {
-  // We draw our own close button so that we could show/hide it when the
-  // mouse moves, and so that in the future we can add an expand button.
+  // We draw our own close button so that we can capture the button presses and
+  // so we can customize its appearance.
   return false;
 }
 
 std::unique_ptr<views::NonClientFrameView>
 CaptionBubble::CreateNonClientFrameView(views::Widget* widget) {
-  auto frame = std::make_unique<CaptionBubbleFrameView>(close_button_);
+  auto frame = std::make_unique<CaptionBubbleFrameView>(
+      close_button_, expand_button_, collapse_button_);
   frame_ = frame.get();
   return frame;
 }
@@ -481,6 +503,15 @@
         CaptionController::SessionEvent::kCloseButtonClicked);
     if (model_)
       model_->Close();
+  } else if (sender == expand_button_ || sender == collapse_button_) {
+    is_expanded_ = !is_expanded_;
+    bool button_had_focus = sender->HasFocus();
+    views::Button* new_button =
+        is_expanded_ ? collapse_button_ : expand_button_;
+    OnIsExpandedChanged();
+    // TODO(crbug.com/1055150): Ensure that the button keeps focus on mac.
+    if (button_had_focus)
+      new_button->RequestFocus();
   }
 }
 
@@ -492,30 +523,36 @@
     model_->SetObserver(this);
 }
 
-void CaptionBubble::OnTextChange() {
+void CaptionBubble::OnTextChanged() {
   DCHECK(model_);
   label_->SetText(base::ASCIIToUTF16(model_->GetFullText()));
   UpdateBubbleAndWaitTextVisibility();
 }
 
-void CaptionBubble::OnErrorChange() {
+void CaptionBubble::OnErrorChanged() {
   DCHECK(model_);
   bool has_error = model_->HasError();
   label_->SetVisible(!has_error);
   error_message_->SetVisible(has_error);
 
-  // The error icon height may be different from the line height, so update the
-  // bubble content height accordingly.
-  UpdateContentSize();
-  UpdateBubbleAndWaitTextVisibility();
+  // The error is only 1 line, so redraw the bubble.
+  Redraw();
+}
+
+void CaptionBubble::OnIsExpandedChanged() {
+  expand_button_->SetVisible(!is_expanded_);
+  collapse_button_->SetVisible(is_expanded_);
+
+  // The change of expanded state may cause the title to change visibility, and
+  // it surely causes the content height to change, so redraw the bubble.
+  Redraw();
 }
 
 void CaptionBubble::UpdateBubbleAndWaitTextVisibility() {
-  DCHECK(model_);
   // Show the wait text if there is room for it and no error.
-  wait_text_->SetVisible(!model_->HasError() &&
-                         label_->GetPreferredSize().height() <
-                             kLineHeightDip * kNumLines * GetTextScaleFactor());
+  wait_text_->SetVisible(model_ && !model_->HasError() &&
+                         GetNumLinesInLabel() <
+                             static_cast<size_t>(GetNumLinesVisible()));
   UpdateBubbleVisibility();
 }
 
@@ -545,7 +582,7 @@
     base::Optional<ui::CaptionStyle> caption_style) {
   caption_style_ = caption_style;
   UpdateTextSize();
-  SizeToContents();
+  Redraw();
 }
 
 size_t CaptionBubble::GetTextIndexOfLineInLabel(size_t line) const {
@@ -556,8 +593,8 @@
   return label_->GetRequiredLines();
 }
 
-const char* CaptionBubble::GetClassName() const {
-  return "CaptionBubble";
+int CaptionBubble::GetNumLinesVisible() {
+  return is_expanded_ ? kNumLinesExpanded : kNumLinesCollapsed;
 }
 
 double CaptionBubble::GetTextScaleFactor() {
@@ -588,23 +625,35 @@
   error_text_->SetLineHeight(kLineHeightDip * textScaleFactor);
   error_icon_->SetImageSize(gfx::Size(kErrorImageSizeDip * textScaleFactor,
                                       kErrorImageSizeDip * textScaleFactor));
-  UpdateContentSize();
 }
 
 void CaptionBubble::UpdateContentSize() {
-  double textScaleFactor = GetTextScaleFactor();
-
-  int content_height = (model_ && model_->HasError())
-                           ? kLineHeightDip * textScaleFactor
-                           : kLineHeightDip * kNumLines * textScaleFactor;
-  content_height += kBottomPaddingDip;
-  content_container_->SetPreferredSize(
-      gfx::Size(kMaxWidthDip - kCloseButtonMarginDip * 2,
-                content_height - kCloseButtonMarginDip * 2));
+  double text_scale_factor = GetTextScaleFactor();
+  int content_height =
+      (model_ && model_->HasError())
+          ? kLineHeightDip * text_scale_factor
+          : kLineHeightDip * GetNumLinesVisible() * text_scale_factor;
+  // The wait text takes up 1 line.
+  int label_height = wait_text_->GetVisible()
+                         ? content_height - kLineHeightDip * text_scale_factor
+                         : content_height;
+  label_->SetPreferredSize(
+      gfx::Size(kMaxWidthDip - kSidePaddingDip, label_height));
+  content_container_->SetPreferredSize(gfx::Size(kMaxWidthDip, content_height));
   SetPreferredSize(
       gfx::Size(kMaxWidthDip, content_height +
                                   close_button_->GetPreferredSize().height() +
-                                  kCloseButtonMarginDip));
+                                  expand_button_->GetPreferredSize().height()));
+}
+
+void CaptionBubble::Redraw() {
+  UpdateBubbleAndWaitTextVisibility();
+  UpdateContentSize();
+  SizeToContents();
+}
+
+const char* CaptionBubble::GetClassName() const {
+  return "CaptionBubble";
 }
 
 std::string CaptionBubble::GetLabelTextForTesting() {
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble.h b/chrome/browser/ui/views/accessibility/caption_bubble.h
index 454ab1c..0015a47 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble.h
+++ b/chrome/browser/ui/views/accessibility/caption_bubble.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_ACCESSIBILITY_CAPTION_BUBBLE_H_
 #define CHROME_BROWSER_UI_VIEWS_ACCESSIBILITY_CAPTION_BUBBLE_H_
 
+#include <memory>
 #include <string>
 
 #include "chrome/browser/ui/views/accessibility/caption_bubble_model.h"
@@ -12,6 +13,10 @@
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/controls/button/button.h"
 
+namespace gfx {
+struct VectorIcon;
+}
+
 namespace views {
 class Label;
 class ImageButton;
@@ -82,7 +87,7 @@
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void AddedToWidget() override;
 
-  // Views::ButtonListener:
+  // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
  private:
@@ -91,20 +96,29 @@
 
   // Called by CaptionBubbleModel to notify this object that the model's text
   // has changed. Sets the text of the caption bubble to the model's text.
-  void OnTextChange();
+  void OnTextChanged();
 
   // Called by CaptionBubbleModel to notify this object that the model's error
   // state has changed. Makes the caption bubble display an error message if
   // the model has an error, otherwise displays the latest text.
-  void OnErrorChange();
+  void OnErrorChanged();
+
+  // Called when the caption bubble expanded state has changed. Changes the
+  // number of lines displayed.
+  void OnIsExpandedChanged();
 
   void UpdateBubbleAndWaitTextVisibility();
   // The caption bubble manages its own visibility based on whether there's
   // space for it to be shown, and if it has an error or text to display.
   void UpdateBubbleVisibility();
   double GetTextScaleFactor();
+  int GetNumLinesVisible();
   void UpdateTextSize();
   void UpdateContentSize();
+  void Redraw();
+  std::unique_ptr<views::ImageButton> BuildImageButton(
+      const gfx::VectorIcon& icon,
+      const int tooltip_text_id);
 
   // Unowned. Owned by views hierarchy.
   views::Label* label_;
@@ -113,6 +127,8 @@
   views::ImageView* error_icon_;
   views::View* error_message_;
   views::ImageButton* close_button_;
+  views::ImageButton* expand_button_;
+  views::ImageButton* collapse_button_;
   CaptionBubbleFrameView* frame_;
   views::View* content_container_;
 
@@ -134,6 +150,9 @@
 
   // A reference to the BrowserView holding this bubble. Unowned.
   BrowserView* browser_view_;
+
+  // Whether the caption bubble is expanded to show more lines of text.
+  bool is_expanded_ = false;
 };
 
 }  // namespace captions
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
index 1bc2c90..ec9779ae 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
+++ b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
@@ -69,6 +69,15 @@
     return controller_ ? controller_->caption_bubble_->close_button_ : nullptr;
   }
 
+  views::Button* GetExpandButton() {
+    return controller_ ? controller_->caption_bubble_->expand_button_ : nullptr;
+  }
+
+  views::Button* GetCollapseButton() {
+    return controller_ ? controller_->caption_bubble_->collapse_button_
+                       : nullptr;
+  }
+
   views::View* GetErrorMessage() {
     return controller_ ? controller_->caption_bubble_->error_message_ : nullptr;
   }
@@ -95,8 +104,7 @@
 
   void DestroyController() { controller_.reset(nullptr); }
 
-  void ClickCloseButton() {
-    views::Button* button = GetCloseButton();
+  void ClickButton(views::Button* button) {
     if (!button)
       return;
     button->OnMousePressed(
@@ -179,16 +187,16 @@
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, LaysOutCaptionLabel) {
   // A short caption is bottom-aligned with the bubble. The bubble bounds
-  // are inset by 24 dip on the the sides and 28 dip on the bottom. The label
+  // are inset by 18 dip on the the sides and 24 dip on the bottom. The label
   // top can change, but the bubble height and width should not change.
   OnPartialTranscription("Cats rock");
   gfx::Rect label_bounds = GetLabel()->GetBoundsInScreen();
   gfx::Rect bubble_bounds = GetBubble()->GetBoundsInScreen();
   int bubble_height = bubble_bounds.height();
   int bubble_width = bubble_bounds.width();
-  EXPECT_EQ(label_bounds.x() - 24, bubble_bounds.x());  // left
-  EXPECT_EQ(label_bounds.right() + 24, bubble_bounds.right());
-  EXPECT_EQ(label_bounds.bottom() + 28, bubble_bounds.bottom());
+  EXPECT_EQ(label_bounds.x() - 18, bubble_bounds.x());  // left
+  EXPECT_EQ(label_bounds.right() + 18, bubble_bounds.right());
+  EXPECT_EQ(label_bounds.bottom() + 24, bubble_bounds.bottom());
 
   // Ensure overflow by using a very long caption, should still be aligned
   // with the bottom of the bubble.
@@ -200,9 +208,9 @@
       "house and, at age 15, she signed her first record deal.");
   label_bounds = GetLabel()->GetBoundsInScreen();
   bubble_bounds = GetBubble()->GetBoundsInScreen();
-  EXPECT_EQ(label_bounds.x() - 24, bubble_bounds.x());  // left
-  EXPECT_EQ(label_bounds.right() + 24, bubble_bounds.right());
-  EXPECT_EQ(label_bounds.bottom() + 28, bubble_bounds.bottom());
+  EXPECT_EQ(label_bounds.x() - 18, bubble_bounds.x());  // left
+  EXPECT_EQ(label_bounds.right() + 18, bubble_bounds.right());
+  EXPECT_EQ(label_bounds.bottom() + 24, bubble_bounds.bottom());
   EXPECT_EQ(bubble_height, bubble_bounds.height());
   EXPECT_EQ(bubble_width, bubble_bounds.width());
 }
@@ -221,6 +229,8 @@
 }
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, BubblePositioning) {
+  int bubble_width = 536;
+  gfx::Insets bubble_margins(6);
   views::View* contents_view =
       BrowserView::GetBrowserViewForBrowser(browser())->GetContentsView();
 
@@ -228,19 +238,22 @@
   OnPartialTranscription("Mantis shrimp have 12-16 photoreceptors");
   ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
                        GetCaptionWidget()->GetClientAreaBoundsInScreen());
-  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), 548);
+  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
+  EXPECT_EQ(GetBubble()->margins(), bubble_margins);
 
   // Move the window and the widget should stay centered.
   browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 600));
   ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
                        GetCaptionWidget()->GetClientAreaBoundsInScreen());
-  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), 548);
+  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
+  EXPECT_EQ(GetBubble()->margins(), bubble_margins);
 
   // Shrink the window's height.
   browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 300));
   ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
                        GetCaptionWidget()->GetClientAreaBoundsInScreen());
-  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), 548);
+  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
+  EXPECT_EQ(GetBubble()->margins(), bubble_margins);
 
   // Shrink it super far, then grow it back up again, and it should still
   // be in the right place.
@@ -248,7 +261,8 @@
   browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 500));
   ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
                        GetCaptionWidget()->GetClientAreaBoundsInScreen());
-  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), 548);
+  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
+  EXPECT_EQ(GetBubble()->margins(), bubble_margins);
 
   // Now shrink the width so that the caption bubble shrinks.
   browser()->window()->SetBounds(gfx::Rect(50, 50, 500, 500));
@@ -256,7 +270,8 @@
   gfx::Rect contents_bounds = contents_view->GetBoundsInScreen();
   ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
                        GetCaptionWidget()->GetClientAreaBoundsInScreen());
-  EXPECT_LT(GetBubble()->GetBoundsInScreen().width(), 548);
+  EXPECT_LT(GetBubble()->GetBoundsInScreen().width(), bubble_width);
+  EXPECT_EQ(GetBubble()->margins(), bubble_margins);
   EXPECT_EQ(20, widget_bounds.x() - contents_bounds.x());
   EXPECT_EQ(20, contents_bounds.right() - widget_bounds.right());
 
@@ -266,7 +281,8 @@
   browser()->window()->SetBounds(gfx::Rect(100, 100, 800, 600));
   ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
                        GetCaptionWidget()->GetClientAreaBoundsInScreen());
-  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), 548);
+  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
+  EXPECT_EQ(GetBubble()->margins(), bubble_margins);
 
   // Now move the widget within the window.
   GetCaptionWidget()->SetBounds(
@@ -274,14 +290,16 @@
                 GetCaptionWidget()->GetWindowBoundsInScreen().height()));
 
   // The bubble width should not have changed.
-  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), 548);
+  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
+  EXPECT_EQ(GetBubble()->margins(), bubble_margins);
 
   // Move the window and the widget stays fixed with respect to the window.
   browser()->window()->SetBounds(gfx::Rect(100, 100, 800, 600));
   widget_bounds = GetCaptionWidget()->GetClientAreaBoundsInScreen();
   EXPECT_EQ(200, widget_bounds.x());
   EXPECT_EQ(300, widget_bounds.y());
-  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), 548);
+  EXPECT_EQ(GetBubble()->GetBoundsInScreen().width(), bubble_width);
+  EXPECT_EQ(GetBubble()->margins(), bubble_margins);
 
   // Now put the window in the top corner for easier math.
   browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 600));
@@ -374,7 +392,7 @@
   EXPECT_TRUE(GetCaptionWidget());
   EXPECT_TRUE(IsWidgetVisible());
   EXPECT_EQ("Elephants have 3-4 toenails per foot", GetLabelText());
-  ClickCloseButton();
+  ClickButton(GetCloseButton());
   EXPECT_TRUE(GetCaptionWidget());
   EXPECT_FALSE(IsWidgetVisible());
   success = OnFinalTranscription(
@@ -459,6 +477,26 @@
                                               false, false, false));
   EXPECT_TRUE(GetCloseButton()->HasFocus());
 
+  // Next tab should be the expand button.
+  EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_TAB, false,
+                                              false, false, false));
+  EXPECT_TRUE(GetExpandButton()->HasFocus());
+
+#if !defined(OS_MACOSX)
+  // Pressing enter should turn the expand button into a collapse button.
+  // Focus should remain on the collapse button.
+  // TODO(crbug.com/1055150): Fix this for Mac.
+  EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_RETURN, false,
+                                              false, false, false));
+  EXPECT_TRUE(GetCollapseButton()->HasFocus());
+
+  // Pressing enter again should turn the collapse button into an expand button.
+  // Focus should remain on the expand button.
+  EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_RETURN, false,
+                                              false, false, false));
+  EXPECT_TRUE(GetExpandButton()->HasFocus());
+#endif
+
   // Next tab exits the bubble entirely.
   EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_TAB, false,
                                               false, false, false));
@@ -605,7 +643,7 @@
 #endif
 
   // Close the bubble. It should not show, even when it has an error.
-  ClickCloseButton();
+  ClickButton(GetCloseButton());
   EXPECT_FALSE(IsWidgetVisible());
   SetHasError(true);
   EXPECT_FALSE(IsWidgetVisible());
@@ -664,7 +702,7 @@
   EXPECT_EQ("Polar bears are the largest carnivores on land", GetLabelText());
 
   // Close caption bubble on tab 0 and verify that it is still visible on tab 1.
-  ClickCloseButton();
+  ClickButton(GetCloseButton());
   EXPECT_FALSE(IsWidgetVisible());
   ActivateTabAt(1);
   EXPECT_TRUE(IsWidgetVisible());
@@ -683,14 +721,14 @@
     text += base::NumberToString(i) + line + " ";
   }
   OnFinalTranscription(text);
-  EXPECT_EQ(text.substr(12500, 15000), GetLabelText());
-  EXPECT_EQ(5u, GetBubble()->GetNumLinesInLabel());
+  EXPECT_EQ(text.substr(10500, 15000), GetLabelText());
+  EXPECT_EQ(9u, GetBubble()->GetNumLinesInLabel());
   OnPartialTranscription(text);
-  EXPECT_EQ(text.substr(12500, 15000) + text, GetLabelText());
-  EXPECT_EQ(35u, GetBubble()->GetNumLinesInLabel());
+  EXPECT_EQ(text.substr(10500, 15000) + text, GetLabelText());
+  EXPECT_EQ(39u, GetBubble()->GetNumLinesInLabel());
   OnFinalTranscription("a ");
-  EXPECT_EQ(text.substr(13000, 15000) + "a ", GetLabelText());
-  EXPECT_EQ(5u, GetBubble()->GetNumLinesInLabel());
+  EXPECT_EQ(text.substr(11000, 15000) + "a ", GetLabelText());
+  EXPECT_EQ(9u, GetBubble()->GetNumLinesInLabel());
 }
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, TabNavigation) {
@@ -750,7 +788,7 @@
 
   // The caption bubble disappears after being closed, and reappears when a
   // transcription is received after a navigation.
-  ClickCloseButton();
+  ClickButton(GetCloseButton());
   EXPECT_FALSE(IsWidgetVisible());
   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
   content::WaitForLoadStop(
@@ -776,8 +814,38 @@
   DestroyController();
 
   OnPartialTranscription("Deer antlers fall off and regrow every year");
-  ClickCloseButton();
+  ClickButton(GetCloseButton());
   DestroyController();
 }
 
+IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, ExpandsAndCollapses) {
+  int line_height = 24;
+
+  OnPartialTranscription("Seahorses are monogamous");
+  EXPECT_TRUE(GetExpandButton()->GetVisible());
+  EXPECT_FALSE(GetCollapseButton()->GetVisible());
+  EXPECT_EQ(line_height, GetLabel()->GetBoundsInScreen().height());
+
+  ClickButton(GetExpandButton());
+  EXPECT_TRUE(GetCollapseButton()->GetVisible());
+  EXPECT_FALSE(GetExpandButton()->GetVisible());
+  EXPECT_EQ(7 * line_height, GetLabel()->GetBoundsInScreen().height());
+
+  // Switch tabs. The bubble should remain expanded.
+  InsertNewTab();
+  ActivateTabAt(1);
+  EXPECT_FALSE(IsWidgetVisible());
+
+  OnPartialTranscription(
+      "Honeybees have tiny hairs on their eyes to help them collect pollen");
+  EXPECT_TRUE(GetCollapseButton()->GetVisible());
+  EXPECT_FALSE(GetExpandButton()->GetVisible());
+  EXPECT_EQ(7 * line_height, GetLabel()->GetBoundsInScreen().height());
+
+  ClickButton(GetCollapseButton());
+  EXPECT_TRUE(GetExpandButton()->GetVisible());
+  EXPECT_FALSE(GetCollapseButton()->GetVisible());
+  EXPECT_EQ(line_height, GetLabel()->GetBoundsInScreen().height());
+}
+
 }  // namespace captions
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_model.cc b/chrome/browser/ui/views/accessibility/caption_bubble_model.cc
index c87af10..b86ad571 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble_model.cc
+++ b/chrome/browser/ui/views/accessibility/caption_bubble_model.cc
@@ -9,9 +9,9 @@
 #include "content/public/browser/web_contents.h"
 
 namespace {
-// The caption bubble contains 2 lines of text in its normal size and 4 lines
-// in its expanded size, so the maximum number of lines before truncating is 5.
-constexpr int kMaxLines = 5;
+// The caption bubble contains 2 lines of text in its normal size and 8 lines
+// in its expanded size, so the maximum number of lines before truncating is 9.
+constexpr int kMaxLines = 9;
 }  // namespace
 
 namespace captions {
@@ -29,8 +29,8 @@
     return;
   observer_ = observer;
   if (observer_) {
-    observer_->OnTextChange();
-    observer_->OnErrorChange();
+    observer_->OnTextChanged();
+    observer_->OnErrorChanged();
   }
 }
 
@@ -38,27 +38,27 @@
   observer_ = nullptr;
 }
 
-void CaptionBubbleModel::OnTextChange() {
+void CaptionBubbleModel::OnTextChanged() {
   if (observer_)
-    observer_->OnTextChange();
+    observer_->OnTextChanged();
 }
 
 void CaptionBubbleModel::SetPartialText(const std::string& partial_text) {
   partial_text_ = partial_text;
-  OnTextChange();
+  OnTextChanged();
 }
 
 void CaptionBubbleModel::Close() {
   final_text_.clear();
   partial_text_.clear();
   is_closed_ = true;
-  OnTextChange();
+  OnTextChanged();
 }
 
 void CaptionBubbleModel::SetHasError(bool has_error) {
   has_error_ = has_error;
   if (observer_)
-    observer_->OnErrorChange();
+    observer_->OnErrorChanged();
 }
 
 void CaptionBubbleModel::DidFinishNavigation(
@@ -71,7 +71,7 @@
   partial_text_.clear();
   is_closed_ = false;
   has_error_ = false;
-  OnTextChange();
+  OnTextChanged();
 }
 
 void CaptionBubbleModel::CommitPartialText() {
@@ -100,7 +100,7 @@
     const size_t truncate_index =
         observer_->GetTextIndexOfLineInLabel(num_lines - kMaxLines);
     final_text_.erase(0, truncate_index);
-    OnTextChange();
+    OnTextChanged();
   }
 }
 
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_model.h b/chrome/browser/ui/views/accessibility/caption_bubble_model.h
index d0e4f90..e1a8765c 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble_model.h
+++ b/chrome/browser/ui/views/accessibility/caption_bubble_model.h
@@ -44,9 +44,6 @@
   void SetObserver(CaptionBubble* observer);
   void RemoveObserver();
 
-  // Alert the observer that a change has occurred to the model text.
-  void OnTextChange();
-
   // Set the partial text and alert the observer.
   void SetPartialText(const std::string& partial_text);
 
@@ -70,6 +67,9 @@
       content::NavigationHandle* navigation_handle) override;
 
  private:
+  // Alert the observer that a change has occurred to the model text.
+  void OnTextChanged();
+
   std::string final_text_;
   std::string partial_text_;
 
diff --git a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc
index 01f30e3..29d23c3 100644
--- a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc
+++ b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc
@@ -8,6 +8,7 @@
 #include "base/metrics/user_metrics.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/sharing/features.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
@@ -58,6 +59,12 @@
   return gfx::Size(kQRImageSizePx, kQRImageSizePx);
 }
 
+// Calculates the height of the QR Code with padding.
+constexpr gfx::Size GetPreferredQRCodeImageSize() {
+  return gfx::Size(kQRImageSizePx + kQRPaddingPx,
+                   kQRImageSizePx + kQRPaddingPx);
+}
+
 // Renders a solid square of color {r, g, b} at 100% alpha.
 gfx::ImageSkia GetPlaceholderImageSkia(const SkColor color) {
   SkBitmap bitmap;
@@ -137,10 +144,12 @@
 void QRCodeGeneratorBubble::OnCodeGeneratorResponse(
     const mojom::GenerateQRCodeResponsePtr response) {
   if (response->error_code != mojom::QRCodeGeneratorError::NONE) {
-    DisplayPlaceholderImage();
+    DisplayError(response->error_code);
     return;
   }
 
+  ShrinkAndHideDisplay(center_error_label_);
+  bottom_error_label_->SetVisible(false);
   gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(response->bitmap);
   UpdateQRImage(image);
 }
@@ -148,14 +157,32 @@
 void QRCodeGeneratorBubble::UpdateQRImage(gfx::ImageSkia qr_image) {
   qr_code_image_->SetImage(qr_image);
   qr_code_image_->SetImageSize(GetQRImageSize());
-  qr_code_image_->SetPreferredSize(GetQRImageSize() +
-                                   gfx::Size(kQRPaddingPx, kQRPaddingPx));
+  qr_code_image_->SetPreferredSize(GetPreferredQRCodeImageSize());
+  qr_code_image_->SetVisible(true);
 }
 
 void QRCodeGeneratorBubble::DisplayPlaceholderImage() {
   UpdateQRImage(GetPlaceholderImageSkia(gfx::kGoogleGrey100));
 }
 
+void QRCodeGeneratorBubble::DisplayError(mojom::QRCodeGeneratorError error) {
+  if (error == mojom::QRCodeGeneratorError::INPUT_TOO_LONG) {
+    ShrinkAndHideDisplay(center_error_label_);
+    DisplayPlaceholderImage();
+    bottom_error_label_->SetVisible(true);
+    return;
+  }
+  ShrinkAndHideDisplay(qr_code_image_);
+  bottom_error_label_->SetVisible(false);
+  center_error_label_->SetPreferredSize(GetPreferredQRCodeImageSize());
+  center_error_label_->SetVisible(true);
+}
+
+void QRCodeGeneratorBubble::ShrinkAndHideDisplay(views::View* view) {
+  view->SetPreferredSize(gfx::Size(0, 0));
+  view->SetVisible(false);
+}
+
 views::View* QRCodeGeneratorBubble::GetInitiallyFocusedView() {
   return textfield_url_;
 }
@@ -184,8 +211,10 @@
 
   // Internal IDs for column layout; no effect on UI.
   constexpr int kQRImageColumnSetId = 0;
-  constexpr int kTextFieldColumnSetId = 1;
-  constexpr int kDownloadRowColumnSetId = 2;
+  constexpr int kCenterErrorLabelColumnSetId = 1;
+  constexpr int kTextFieldColumnSetId = 2;
+  constexpr int kBottomErrorLabelColumnSetId = 3;
+  constexpr int kDownloadRowColumnSetId = 4;
 
   // Add top-level Grid Layout manager for this dialog.
   views::GridLayout* const layout =
@@ -207,14 +236,30 @@
   qr_code_image->SetHorizontalAlignment(Alignment::kCenter);
   qr_code_image->SetVerticalAlignment(Alignment::kCenter);
   qr_code_image->SetImageSize(GetQRImageSize());
-  qr_code_image->SetPreferredSize(GetQRImageSize() +
-                                  gfx::Size(kQRPaddingPx, kQRPaddingPx));
+  qr_code_image->SetPreferredSize(GetPreferredQRCodeImageSize());
   qr_code_image->SetBackground(
       views::CreateRoundedRectBackground(SK_ColorWHITE, border_radius));
 
   layout->StartRow(views::GridLayout::kFixedSize, kQRImageColumnSetId);
   qr_code_image_ = layout->AddView(std::move(qr_code_image));
-  DisplayPlaceholderImage();
+
+  // Center error message.
+  views::ColumnSet* column_set_center_error_label =
+      layout->AddColumnSet(kCenterErrorLabelColumnSetId);
+  column_set_center_error_label->AddColumn(
+      views::GridLayout::CENTER, views::GridLayout::CENTER, 1.0,
+      views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
+  auto center_error_label = std::make_unique<views::Label>();
+  center_error_label->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+  center_error_label->SetVerticalAlignment(gfx::ALIGN_MIDDLE);
+  center_error_label->SetEnabledColor(
+      center_error_label->GetNativeTheme()->GetSystemColor(
+          ui::NativeTheme::kColorId_LabelSecondaryColor));
+  center_error_label->SetText(l10n_util::GetStringUTF16(
+      IDS_BROWSER_SHARING_QR_CODE_DIALOG_ERROR_UNKNOWN));
+  layout->StartRow(views::GridLayout::kFixedSize, kCenterErrorLabelColumnSetId);
+  center_error_label_ = layout->AddView(std::move(center_error_label));
+  ShrinkAndHideDisplay(center_error_label_);
 
   // Padding
   AddSmallPaddingRow(layout);
@@ -239,11 +284,33 @@
   layout->StartRow(views::GridLayout::kFixedSize, kTextFieldColumnSetId);
   textfield_url_ = layout->AddView(std::move(textfield_url));
 
+  // Lower error message.
+  views::ColumnSet* column_set_bottom_error_label =
+      layout->AddColumnSet(kBottomErrorLabelColumnSetId);
+  column_set_bottom_error_label->AddColumn(
+      views::GridLayout::FILL, views::GridLayout::CENTER, 1.0,
+      views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
+  auto bottom_error_label = std::make_unique<views::Label>();
+  bottom_error_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  bottom_error_label->SetEnabledColor(
+      bottom_error_label->GetNativeTheme()->GetSystemColor(
+          ui::NativeTheme::kColorId_LabelSecondaryColor));
+  int maxUrlLength = base::GetFieldTrialParamByFeatureAsInt(
+      kSharingQRCodeGenerator, "max_url_length", 122);
+  bottom_error_label->SetText(l10n_util::GetStringFUTF16Int(
+      IDS_BROWSER_SHARING_QR_CODE_DIALOG_ERROR_TOO_LONG, maxUrlLength));
+  bottom_error_label->SetVisible(false);
+  layout->StartRow(views::GridLayout::kFixedSize, kBottomErrorLabelColumnSetId);
+  bottom_error_label_ = layout->AddView(std::move(bottom_error_label));
+  // Updating the image requires both error labels to be initialized.
+  DisplayPlaceholderImage();
+
   // Padding - larger between controls and action buttons.
   layout->AddPaddingRow(
       views::GridLayout::kFixedSize,
       ChromeLayoutProvider::Get()->GetDistanceMetric(
-          views::DISTANCE_DIALOG_CONTENT_MARGIN_BOTTOM_CONTROL));
+          views::DISTANCE_DIALOG_CONTENT_MARGIN_BOTTOM_CONTROL) -
+          bottom_error_label_->GetPreferredSize().height());
 
   // Controls row: tooltip and download button.
   views::ColumnSet* control_columns =
diff --git a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.h b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.h
index 7668e5ea..5de4694 100644
--- a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.h
+++ b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.h
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
 #include "chrome/services/qrcode_generator/public/cpp/qrcode_generator_service.h"
 #include "ui/views/controls/button/button.h"
+#include "ui/views/controls/label.h"
 #include "ui/views/controls/textfield/textfield_controller.h"
 #include "url/gurl.h"
 
@@ -66,6 +67,12 @@
   // Updates the central QR code image with a placeholder.
   void DisplayPlaceholderImage();
 
+  // Shows an error message.
+  void DisplayError(mojom::QRCodeGeneratorError error);
+
+  // Shrinks the view and sets it not visible.
+  void ShrinkAndHideDisplay(views::View* view);
+
   // LocationBarBubbleDelegateView:
   View* GetInitiallyFocusedView() override;
   bool ShouldShowCloseButton() const override;
@@ -101,6 +108,8 @@
   views::Textfield* textfield_url_ = nullptr;
   views::LabelButton* download_button_ = nullptr;
   views::TooltipIcon* tooltip_icon_ = nullptr;
+  views::Label* center_error_label_ = nullptr;
+  views::Label* bottom_error_label_ = nullptr;
 
   QRCodeGeneratorBubbleController* controller_;  // weak.
   content::WebContents* web_contents_;           // weak.
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 84ce278a..b1bc3028 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -704,17 +704,18 @@
     // Move the dragged tabs to their ideal bounds.
     tab_strip_->UpdateIdealBounds();
 
-    // Sets the bounds of the dragged tabs.
-    for (size_t i = 0; i < views.size(); ++i) {
-      // Non-tabs, such as TabGroupHeaders, may also be dragging. Ignore these,
-      // since they are positioned independently.
-      if (views[i]->GetTabSlotViewType() != TabSlotView::ViewType::kTab)
-        continue;
-
-      int tab_data_index = GetIndexOf(views[i]);
-      DCHECK_NE(TabStripModel::kNoTab, tab_data_index);
-      views[i]->SetBoundsRect(ideal_bounds(tab_data_index));
+    // Sets the bounds of the dragged tab slots.
+    for (TabSlotView* view : views) {
+      if (view->GetTabSlotViewType() ==
+          TabSlotView::ViewType::kTabGroupHeader) {
+        view->SetBoundsRect(ideal_bounds(view->group().value()));
+      } else {
+        int tab_data_index = GetIndexOf(view);
+        DCHECK_NE(TabStripModel::kNoTab, tab_data_index);
+        view->SetBoundsRect(ideal_bounds(tab_data_index));
+      }
     }
+
     tab_strip_->SetTabSlotVisibility();
     tab_strip_->SchedulePaint();
   }
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
index 8554f6e..6fc75be 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
@@ -151,48 +151,8 @@
     AppMenuIconController::TypeAndSeverity type_and_severity) {
   type_and_severity_ = type_and_severity;
 
-  int message_id;
-  base::string16 text;
-  if (type_and_severity.severity == AppMenuIconController::Severity::NONE) {
-    message_id = IDS_APPMENU_TOOLTIP;
-  } else if (type_and_severity.type ==
-             AppMenuIconController::IconType::UPGRADE_NOTIFICATION) {
-    message_id = IDS_APPMENU_TOOLTIP_UPDATE_AVAILABLE;
-    text = l10n_util::GetStringUTF16(IDS_APP_MENU_BUTTON_UPDATE);
-  } else {
-    message_id = IDS_APPMENU_TOOLTIP_ALERT;
-    text = l10n_util::GetStringUTF16(IDS_APP_MENU_BUTTON_ERROR);
-  }
-
-  base::Optional<SkColor> color;
-  switch (type_and_severity.severity) {
-    case AppMenuIconController::Severity::NONE:
-      break;
-    case AppMenuIconController::Severity::LOW:
-      color = AdjustHighlightColorForContrast(
-          GetThemeProvider(), gfx::kGoogleGreen300, gfx::kGoogleGreen600,
-          gfx::kGoogleGreen050, gfx::kGoogleGreen900);
-
-      break;
-    case AppMenuIconController::Severity::MEDIUM:
-      color = AdjustHighlightColorForContrast(
-          GetThemeProvider(), gfx::kGoogleYellow300, gfx::kGoogleYellow600,
-          gfx::kGoogleYellow050, gfx::kGoogleYellow900);
-
-      break;
-    case AppMenuIconController::Severity::HIGH:
-      color = AdjustHighlightColorForContrast(
-          GetThemeProvider(), gfx::kGoogleRed300, gfx::kGoogleRed600,
-          gfx::kGoogleRed050, gfx::kGoogleRed900);
-
-      break;
-  }
-
-  if (base::FeatureList::IsEnabled(features::kUseTextForUpdateButton))
-    SetHighlight(text, color);
-
-  SetTooltipText(l10n_util::GetStringUTF16(message_id));
   UpdateIcon();
+  UpdateTextAndHighlightColor();
 }
 
 void BrowserAppMenuButton::SetPromoFeature(
@@ -251,6 +211,11 @@
       browser, run_types, alert_reopen_tab_items);
 }
 
+void BrowserAppMenuButton::OnThemeChanged() {
+  UpdateTextAndHighlightColor();
+  AppMenuButton::OnThemeChanged();
+}
+
 void BrowserAppMenuButton::UpdateIcon() {
   bool touch_ui = ui::TouchUiController::Get()->touch_ui();
   if (base::FeatureList::IsEnabled(features::kUseTextForUpdateButton)) {
@@ -270,6 +235,50 @@
   }
 }
 
+void BrowserAppMenuButton::UpdateTextAndHighlightColor() {
+  int tooltip_message_id;
+  base::string16 text;
+  if (type_and_severity_.severity == AppMenuIconController::Severity::NONE) {
+    tooltip_message_id = IDS_APPMENU_TOOLTIP;
+  } else if (type_and_severity_.type ==
+             AppMenuIconController::IconType::UPGRADE_NOTIFICATION) {
+    tooltip_message_id = IDS_APPMENU_TOOLTIP_UPDATE_AVAILABLE;
+    text = l10n_util::GetStringUTF16(IDS_APP_MENU_BUTTON_UPDATE);
+  } else {
+    tooltip_message_id = IDS_APPMENU_TOOLTIP_ALERT;
+    text = l10n_util::GetStringUTF16(IDS_APP_MENU_BUTTON_ERROR);
+  }
+
+  base::Optional<SkColor> color;
+  switch (type_and_severity_.severity) {
+    case AppMenuIconController::Severity::NONE:
+      break;
+    case AppMenuIconController::Severity::LOW:
+      color = AdjustHighlightColorForContrast(
+          GetThemeProvider(), gfx::kGoogleGreen300, gfx::kGoogleGreen600,
+          gfx::kGoogleGreen050, gfx::kGoogleGreen900);
+
+      break;
+    case AppMenuIconController::Severity::MEDIUM:
+      color = AdjustHighlightColorForContrast(
+          GetThemeProvider(), gfx::kGoogleYellow300, gfx::kGoogleYellow600,
+          gfx::kGoogleYellow050, gfx::kGoogleYellow900);
+
+      break;
+    case AppMenuIconController::Severity::HIGH:
+      color = AdjustHighlightColorForContrast(
+          GetThemeProvider(), gfx::kGoogleRed300, gfx::kGoogleRed600,
+          gfx::kGoogleRed050, gfx::kGoogleRed900);
+
+      break;
+  }
+
+  SetTooltipText(l10n_util::GetStringUTF16(tooltip_message_id));
+
+  if (base::FeatureList::IsEnabled(features::kUseTextForUpdateButton))
+    SetHighlight(text, color);
+}
+
 const char* BrowserAppMenuButton::GetClassName() const {
   return "BrowserAppMenuButton";
 }
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
index 5169533..aa3a100 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
@@ -63,6 +63,7 @@
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
   SkColor GetInkDropBaseColor() const override;
   base::string16 GetTooltipText(const gfx::Point& p) const override;
+  void OnThemeChanged() override;
   // Updates the presentation according to |severity_| and the theme provider.
   void UpdateIcon() override;
 
@@ -75,6 +76,8 @@
  private:
   void OnTouchUiChanged();
 
+  void UpdateTextAndHighlightColor();
+
   AppMenuIconController::TypeAndSeverity type_and_severity_{
       AppMenuIconController::IconType::NONE,
       AppMenuIconController::Severity::NONE};
diff --git a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc
index 83fc0603..423f320 100644
--- a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc
+++ b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/check_op.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
@@ -983,12 +984,12 @@
   dialog.ShowDialogAsync();
 
   if (listener) {
-    listener.Run(base::Bind(&TryChromeDialog::OnProcessNotification,
-                            base::Unretained(&dialog)));
+    listener.Run(base::BindRepeating(&TryChromeDialog::OnProcessNotification,
+                                     base::Unretained(&dialog)));
   }
   run_loop.Run();
   if (listener)
-    listener.Run(base::Closure());
+    listener.Run(base::NullCallback());
 
   return dialog.result();
 }
diff --git a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.h b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.h
index 3a38122..a31836d8 100644
--- a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.h
+++ b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.h
@@ -47,7 +47,8 @@
   // Receives a closure to run upon process singleton notification when the
   // modal dialog is open, or a null closure when the active dialog is
   // dismissed.
-  using ActiveModalDialogListener = base::Callback<void(base::Closure)>;
+  using ActiveModalDialogListener =
+      base::RepeatingCallback<void(base::RepeatingClosure)>;
 
   enum Result {
     NOT_NOW,                    // Don't launch chrome. Exit now.
diff --git a/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
index ae20e571..7edc3ee0 100644
--- a/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h"
 
+#include <string>
+
 #include "base/feature_list.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
@@ -246,7 +248,15 @@
 void AddNearbyShareData(content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
       {"nearbyShareTitle", IDS_SETTINGS_NEARBY_SHARE_TITLE},
-  };
+      {"nearbyShareDeviceNameRowTitle",
+       IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ROW_TITLE},
+      {"nearbyShareDeviceNameDialogTitle",
+       IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_DIALOG_TITLE},
+      {"nearbyShareDeviceNameDialogInputLabel",
+       IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_INPUT_LABEL},
+      {"nearbyShareEditDeviceName", IDS_SETTINGS_NEARBY_SHARE_EDIT_DEVICE_NAME},
+      {"nearbyShareDeviceNameAriaDescription",
+       IDS_SETTINGS_NEARBY_SHARE_DEVICE_NAME_ARIA_DESCRIPTION}};
   AddLocalizedStringsBulk(html_source, kLocalizedStrings);
 
   html_source->AddBoolean(
diff --git a/chrome/browser/unload_browsertest.cc b/chrome/browser/unload_browsertest.cc
index ff1fac4..845d1591 100644
--- a/chrome/browser/unload_browsertest.cc
+++ b/chrome/browser/unload_browsertest.cc
@@ -212,9 +212,10 @@
     UnloadResults unload_results;
     BrowserList::CloseAllBrowsersWithProfile(
         browser()->profile(),
-        base::Bind(&UnloadResults::AddSuccess,
-                   base::Unretained(&unload_results)),
-        base::Bind(&UnloadResults::AddAbort, base::Unretained(&unload_results)),
+        base::BindRepeating(&UnloadResults::AddSuccess,
+                            base::Unretained(&unload_results)),
+        base::BindRepeating(&UnloadResults::AddAbort,
+                            base::Unretained(&unload_results)),
         force);
     ui_test_utils::WaitForBrowserToClose();
     EXPECT_EQ(1, unload_results.get_successes());
@@ -351,8 +352,10 @@
   UnloadResults unload_results;
   BrowserList::CloseAllBrowsersWithProfile(
       browser()->profile(),
-      base::Bind(&UnloadResults::AddSuccess, base::Unretained(&unload_results)),
-      base::Bind(&UnloadResults::AddAbort, base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddSuccess,
+                          base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddAbort,
+                          base::Unretained(&unload_results)),
       false);
   ClickModalDialogButton(true);
   ui_test_utils::WaitForBrowserToClose();
@@ -381,8 +384,10 @@
   UnloadResults unload_results;
   BrowserList::CloseAllBrowsersWithProfile(
       browser()->profile(),
-      base::Bind(&UnloadResults::AddSuccess, base::Unretained(&unload_results)),
-      base::Bind(&UnloadResults::AddAbort, base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddSuccess,
+                          base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddAbort,
+                          base::Unretained(&unload_results)),
       false);
 
   // We wait for the title to change after cancelling the closure of browser
@@ -410,13 +415,17 @@
   UnloadResults unload_results;
   BrowserList::CloseAllBrowsersWithProfile(
       browser()->profile(),
-      base::Bind(&UnloadResults::AddSuccess, base::Unretained(&unload_results)),
-      base::Bind(&UnloadResults::AddAbort, base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddSuccess,
+                          base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddAbort,
+                          base::Unretained(&unload_results)),
       false);
   BrowserList::CloseAllBrowsersWithProfile(
       browser()->profile(),
-      base::Bind(&UnloadResults::AddSuccess, base::Unretained(&unload_results)),
-      base::Bind(&UnloadResults::AddAbort, base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddSuccess,
+                          base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddAbort,
+                          base::Unretained(&unload_results)),
       false);
   ClickModalDialogButton(true);
   ui_test_utils::WaitForBrowserToClose();
@@ -433,13 +442,17 @@
   UnloadResults unload_results;
   BrowserList::CloseAllBrowsersWithProfile(
       browser()->profile(),
-      base::Bind(&UnloadResults::AddSuccess, base::Unretained(&unload_results)),
-      base::Bind(&UnloadResults::AddAbort, base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddSuccess,
+                          base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddAbort,
+                          base::Unretained(&unload_results)),
       false);
   BrowserList::CloseAllBrowsersWithProfile(
       browser()->profile(),
-      base::Bind(&UnloadResults::AddSuccess, base::Unretained(&unload_results)),
-      base::Bind(&UnloadResults::AddAbort, base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddSuccess,
+                          base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddAbort,
+                          base::Unretained(&unload_results)),
       false);
 
   // We wait for the title to change after cancelling the closure of browser
@@ -609,13 +622,17 @@
   UnloadResults unload_results;
   BrowserList::CloseAllBrowsersWithProfile(
       browser()->profile(),
-      base::Bind(&UnloadResults::AddSuccess, base::Unretained(&unload_results)),
-      base::Bind(&UnloadResults::AddAbort, base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddSuccess,
+                          base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddAbort,
+                          base::Unretained(&unload_results)),
       false);
   BrowserList::CloseAllBrowsersWithProfile(
       browser()->profile(),
-      base::Bind(&UnloadResults::AddSuccess, base::Unretained(&unload_results)),
-      base::Bind(&UnloadResults::AddAbort, base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddSuccess,
+                          base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddAbort,
+                          base::Unretained(&unload_results)),
       true);
   ui_test_utils::WaitForBrowserToClose();
   EXPECT_EQ(1, unload_results.get_successes());
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index a84cb13d..99ce577 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1594922139-2e74d5a60ec66c9d9f020f69b34f84d613c708fd.profdata
+chrome-mac-master-1594943787-51a385cad6026d977d7d0e8c786de56f21cce493.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index a89f192..41c7b9a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1594814211-6e9c837aecb05743d0a6513baf4c13c15785a909.profdata
+chrome-win64-master-1594878809-f70bebc20790ac0846ac45f570a81eb656b74862.profdata
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index ea4b1fa0..3b595e4 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -317,6 +317,10 @@
     "dependencies": ["manifest:devtools_page"],
     "contexts": ["blessed_extension"]
   },
+  "documentScan": {
+    "dependencies": ["permission:documentScan"],
+    "contexts": ["blessed_extension"]
+  },
   "downloads": {
     "dependencies": ["permission:downloads"],
     "contexts": ["blessed_extension"]
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 2223083..61277a68 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -237,6 +237,11 @@
     "channel": "stable",
     "extension_types": ["extension", "platform_app"]
   },
+  "documentScan": {
+    "channel": "stable",
+    "extension_types": ["extension", "platform_app"],
+    "platforms": ["chromeos"]
+  },
   "downloads": {
     "channel": "stable",
     "extension_types": ["extension"]
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni
index b8f3326..a858047 100644
--- a/chrome/common/extensions/api/api_sources.gni
+++ b/chrome/common/extensions/api/api_sources.gni
@@ -80,6 +80,7 @@
   schema_sources_ += [
     "certificate_provider.idl",
     "certificate_provider_internal.idl",
+    "document_scan.idl",
     "echo_private.json",
     "enterprise_device_attributes.idl",
     "enterprise_networking_attributes.idl",
diff --git a/extensions/common/api/document_scan.idl b/chrome/common/extensions/api/document_scan.idl
similarity index 91%
rename from extensions/common/api/document_scan.idl
rename to chrome/common/extensions/api/document_scan.idl
index a2bdcca..2310b8e 100644
--- a/extensions/common/api/document_scan.idl
+++ b/chrome/common/extensions/api/document_scan.idl
@@ -4,6 +4,8 @@
 
 // Use the <code>chrome.documentScan</code> API to discover and retrieve
 // images from attached paper document scanners.
+[platforms=("chromeos"),
+implemented_in="chrome/browser/chromeos/extensions/document_scan/document_scan_api.h"]
 namespace documentScan {
   dictionary ScanOptions {
     // The MIME types that are accepted by the caller.
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
index 00aa1c2..39c75e4 100644
--- a/chrome/common/extensions/permissions/chrome_api_permissions.cc
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -36,6 +36,7 @@
     {APIPermission::kDeclarativeContent, "declarativeContent"},
     {APIPermission::kDesktopCapture, "desktopCapture"},
     {APIPermission::kDesktopCapturePrivate, "desktopCapturePrivate"},
+    {APIPermission::kDocumentScan, "documentScan"},
     {APIPermission::kDownloads, "downloads"},
     {APIPermission::kDownloadsOpen, "downloads.open"},
     {APIPermission::kDownloadsShelf, "downloads.shelf"},
diff --git a/chrome/installer/mini_installer/decompress.cc b/chrome/installer/mini_installer/decompress.cc
index a9b97b69..e314cb5 100644
--- a/chrome/installer/mini_installer/decompress.cc
+++ b/chrome/installer/mini_installer/decompress.cc
@@ -2,17 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <windows.h>  // NOLINT
+#include "chrome/installer/mini_installer/decompress.h"
+
+#include <windows.h>
 
 #include <fcntl.h>  // for _O_* constants
 #include <fdi.h>
 #include <stddef.h>
 #include <stdlib.h>
 
-#include "chrome/installer/mini_installer/decompress.h"
-
 namespace {
 
+// A simple struct to hold data passed to and from FDICopy via its |pvUser|
+// argument.
+struct ExpandContext {
+  // The path to the single destination file.
+  const wchar_t* const dest_path;
+
+  // Set to true if the file was extracted to |dest_path|.
+  bool succeeded;
+};
+
 FNALLOC(Alloc) {
   return ::HeapAlloc(::GetProcessHeap(), 0, cb);
 }
@@ -115,54 +125,50 @@
 }
 
 FNFDINOTIFY(Notify) {
-  INT_PTR result = 0;
-
   // Since we will only ever be decompressing a single file at a time
   // we take a shortcut and provide a pointer to the wide destination file
   // of the file we want to write.  This way we don't have to bother with
   // utf8/wide conversion and concatenation of directory and file name.
-  const wchar_t* destination = reinterpret_cast<const wchar_t*>(pfdin->pv);
+  ExpandContext& context = *reinterpret_cast<ExpandContext*>(pfdin->pv);
 
   switch (fdint) {
-    case fdintCOPY_FILE: {
-      result = reinterpret_cast<INT_PTR>(
-          ::CreateFileW(destination, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
-                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
-      break;
-    }
+    case fdintCOPY_FILE:
+      // By sheer coincidence, CreateFileW's success/failure results match that
+      // of fdintCOPY_FILE.
+      return reinterpret_cast<INT_PTR>(::CreateFileW(
+          context.dest_path, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
+          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
 
     case fdintCLOSE_FILE_INFO: {
+      // Set the file's creation time and file attributes.
+      FILE_BASIC_INFO info = {};
       FILETIME file_time;
       FILETIME local;
-      // Converts MS-DOS date and time values to a file time
       if (DosDateTimeToFileTime(pfdin->date, pfdin->time, &file_time) &&
           LocalFileTimeToFileTime(&file_time, &local)) {
-        SetFileTime(reinterpret_cast<HANDLE>(pfdin->hf), &local, nullptr,
-                    nullptr);
+        info.CreationTime.u.LowPart = local.dwLowDateTime;
+        info.CreationTime.u.HighPart = local.dwHighDateTime;
       }
+      info.FileAttributes =
+          pfdin->attribs & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
+                            FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE);
+      ::SetFileInformationByHandle(reinterpret_cast<HANDLE>(pfdin->hf),
+                                   FileBasicInfo, &info, sizeof(info));
 
-      result = !Close(pfdin->hf);
-      pfdin->attribs &= FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
-                        FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE;
-      ::SetFileAttributes(destination, pfdin->attribs);
-      break;
+      ::CloseHandle(reinterpret_cast<HANDLE>(pfdin->hf));
+      context.succeeded = true;
+      return -1;  // Break: the one file was extracted.
     }
 
     case fdintCABINET_INFO:
     case fdintENUMERATE:
-      // OK. continue as normal.
-      result = 0;
-      break;
+      return 0;  // Continue: success.
 
     case fdintPARTIAL_FILE:
     case fdintNEXT_CABINET:
     default:
-      // Error case.
-      result = -1;
-      break;
+      return -1;  // Break: error.
   }
-
-  return result;
 }
 
 // Module handle of cabinet.dll
@@ -250,24 +256,20 @@
   // The directory part is assumed to have a trailing backslash.
   scoped_ptr<char> source_path_utf8(WideToUtf8(source, source_name - source));
 
-  scoped_ptr<char> dest_utf8(WideToUtf8(destination, -1));
-  if (!dest_utf8 || !source_name_utf8 || !source_path_utf8)
+  if (!source_name_utf8 || !source_path_utf8)
     return false;
 
-  bool success = false;
-
   ERF erf = {0};
   HFDI fdi = g_FDICreate(&Alloc, &Free, &Open, &Read, &Write, &Close, &Seek,
                          cpuUNKNOWN, &erf);
-  if (fdi) {
-    if (g_FDICopy(fdi, source_name_utf8, source_path_utf8, 0, &Notify, nullptr,
-                  const_cast<wchar_t*>(destination))) {
-      success = true;
-    }
-    g_FDIDestroy(fdi);
-  }
+  if (!fdi)
+    return false;
 
-  return success;
+  ExpandContext context = {destination, /*succeeded=*/false};
+  g_FDICopy(fdi, source_name_utf8, source_path_utf8, 0, &Notify, nullptr,
+            &context);
+  g_FDIDestroy(fdi);
+  return context.succeeded;
 }
 
 }  // namespace mini_installer
diff --git a/chrome/test/data/webui/settings/chromeos/nearby_share_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/nearby_share_subpage_tests.js
index c697165..59e9e02 100644
--- a/chrome/test/data/webui/settings/chromeos/nearby_share_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/nearby_share_subpage_tests.js
@@ -25,6 +25,9 @@
       'nearby_sharing': {
         'enabled': {
           value: true,
+        },
+        'device_name': {
+          value: '',
         }
       }
     };
@@ -64,4 +67,18 @@
     assertEquals(false, subpage.prefs.nearby_sharing.enabled.value);
     assertEquals('Off', onOffText.textContent.trim());
   });
+
+  test('update device name preference', function() {
+    assertEquals('', subpage.prefs.nearby_sharing.device_name.value);
+
+    subpage.$$('#editDeviceNameButton').click();
+    Polymer.dom.flush();
+
+    const dialog = subpage.$$('nearby-share-device-name-dialog');
+    const newName = 'NEW NAME';
+    dialog.$$('cr-input').value = newName;
+    dialog.$$('.action-button').click();
+
+    assertEquals(newName, subpage.prefs.nearby_sharing.device_name.value);
+  });
 });
diff --git a/chromecast/browser/DEPS b/chromecast/browser/DEPS
index 657b89c..564ce4d 100644
--- a/chromecast/browser/DEPS
+++ b/chromecast/browser/DEPS
@@ -61,6 +61,7 @@
   "+services/service_manager/embedder",
   "+storage/browser/quota/quota_settings.h",
   "+third_party/blink/public/common",
+  "+third_party/blink/public/mojom/autoplay",
   "+third_party/blink/public/mojom/loader/resource_load_info.mojom.h",
   "+third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h",
   "+third_party/blink/public/mojom/messaging",
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 49962d6..83ecd27 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -311,10 +311,10 @@
 #if BUILDFLAG(IS_CAST_AUDIO_ONLY)
     {switches::kDisableGpu, ""},
     {switches::kDisableSoftwareRasterizer, ""},
+    {switches::kDisableGpuCompositing, ""},
 #if defined(OS_ANDROID)
     {switches::kDisableFrameRateLimit, ""},
     {switches::kDisableGLDrawingForTests, ""},
-    {switches::kDisableGpuCompositing, ""},
     {cc::switches::kDisableThreadedAnimation, ""},
 #endif  // defined(OS_ANDROID)
 #endif  // BUILDFLAG(IS_CAST_AUDIO_ONLY)
@@ -345,9 +345,6 @@
     {switches::kEnableUseZoomForDSF, "false"},
     // TODO(halliwell): Revert after fix for b/63101386.
     {switches::kDisallowNonExactResourceReuse, ""},
-    // Enable autoplay without requiring any user gesture.
-    {switches::kAutoplayPolicy,
-     switches::autoplay::kNoUserGestureRequiredPolicy},
     // Disable pinch zoom gesture.
     {switches::kDisablePinch, ""},
 };
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 8c3ac993..ab216fc 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -118,10 +118,8 @@
 #include "extensions/browser/extension_message_filter.h"           // nogncheck
 #include "extensions/browser/extension_protocols.h"                // nogncheck
 #include "extensions/browser/extension_registry.h"                 // nogncheck
-#include "extensions/browser/extension_system.h"                   // nogncheck
 #include "extensions/browser/guest_view/extensions_guest_view_message_filter.h"  // nogncheck
 #include "extensions/browser/guest_view/web_view/web_view_guest.h"  // nogncheck
-#include "extensions/browser/info_map.h"                            // nogncheck
 #include "extensions/browser/process_map.h"                         // nogncheck
 #include "extensions/common/constants.h"                            // nogncheck
 #endif
@@ -417,19 +415,9 @@
           site_instance->GetSiteURL());
   if (!extension)
     return;
-  extensions::ExtensionSystem* extension_system =
-      extensions::ExtensionSystem::Get(
-          cast_browser_main_parts_->browser_context());
-
   extensions::ProcessMap::Get(cast_browser_main_parts_->browser_context())
       ->Insert(extension->id(), site_instance->GetProcess()->GetID(),
                site_instance->GetId());
-
-  base::PostTask(FROM_HERE, {content::BrowserThread::IO},
-                 base::BindOnce(&extensions::InfoMap::RegisterExtensionProcess,
-                                extension_system->info_map(), extension->id(),
-                                site_instance->GetProcess()->GetID(),
-                                site_instance->GetId()));
 #endif
 }
 
diff --git a/chromecast/browser/cast_display_configurator.cc b/chromecast/browser/cast_display_configurator.cc
index dfee32f..b5e1cc91 100644
--- a/chromecast/browser/cast_display_configurator.cc
+++ b/chromecast/browser/cast_display_configurator.cc
@@ -133,7 +133,10 @@
 
   display::DisplayConfigurationParams display_config_params(
       display_->display_id(), gfx::Point(), display_->native_mode());
-  delegate_->Configure(display_config_params, std::move(callback));
+  std::vector<display::DisplayConfigurationParams> config_request;
+  config_request.push_back(std::move(display_config_params));
+
+  delegate_->Configure(config_request, std::move(callback));
 }
 
 void CastDisplayConfigurator::DisableDisplay(
@@ -143,7 +146,10 @@
 
   display::DisplayConfigurationParams display_config_params(
       display_->display_id(), gfx::Point(), nullptr);
-  delegate_->Configure(display_config_params, std::move(callback));
+  std::vector<display::DisplayConfigurationParams> config_request;
+  config_request.push_back(std::move(display_config_params));
+
+  delegate_->Configure(config_request, std::move(callback));
 }
 
 void CastDisplayConfigurator::ConfigureDisplayFromCommandLine() {
@@ -210,8 +216,11 @@
 
   display::DisplayConfigurationParams display_config_params(
       display_->display_id(), origin, display_->native_mode());
+  std::vector<display::DisplayConfigurationParams> config_request;
+  config_request.push_back(std::move(display_config_params));
+
   delegate_->Configure(
-      display_config_params,
+      config_request,
       base::BindRepeating(&CastDisplayConfigurator::OnDisplayConfigured,
                           weak_factory_.GetWeakPtr(), display_,
                           display_->native_mode(), origin));
@@ -221,12 +230,14 @@
     display::DisplaySnapshot* display,
     const display::DisplayMode* mode,
     const gfx::Point& origin,
-    bool success) {
+    const base::flat_map<int64_t, bool>& statuses) {
   DCHECK(display);
   DCHECK(mode);
   DCHECK_EQ(display, display_);
+  DCHECK_EQ(statuses.size(), 1UL);
 
   const gfx::Rect bounds(origin, mode->size());
+  bool success = statuses.at(display_->display_id());
   DVLOG(1) << __func__ << " success=" << success
            << " bounds=" << bounds.ToString();
   if (success) {
diff --git a/chromecast/browser/cast_display_configurator.h b/chromecast/browser/cast_display_configurator.h
index cdb14e4a..1bac70ab 100644
--- a/chromecast/browser/cast_display_configurator.h
+++ b/chromecast/browser/cast_display_configurator.h
@@ -8,9 +8,11 @@
 #include <memory>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "ui/display/display.h"
+#include "ui/display/types/display_configuration_params.h"
 #include "ui/display/types/native_display_delegate.h"
 #include "ui/display/types/native_display_observer.h"
 
@@ -62,7 +64,7 @@
   void OnDisplayConfigured(display::DisplaySnapshot* display,
                            const display::DisplayMode* mode,
                            const gfx::Point& origin,
-                           bool success);
+                           const base::flat_map<int64_t, bool>& statuses);
   void UpdateScreen(int64_t display_id,
                     const gfx::Rect& bounds,
                     float device_scale_factor,
diff --git a/chromecast/browser/cast_extension_url_loader_factory.cc b/chromecast/browser/cast_extension_url_loader_factory.cc
index fd0def2..7f220b5 100644
--- a/chromecast/browser/cast_extension_url_loader_factory.cc
+++ b/chromecast/browser/cast_extension_url_loader_factory.cc
@@ -15,7 +15,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
 #include "extensions/browser/extension_registry.h"
-#include "extensions/browser/info_map.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
index a324304..7f224f9 100644
--- a/chromecast/browser/cast_web_contents_impl.cc
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -41,6 +41,7 @@
 #include "net/base/net_errors.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/mojom/autoplay/autoplay.mojom.h"
 #include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -629,6 +630,26 @@
   DCHECK(navigation_handle);
   if (!web_contents_ || closing_ || stopped_)
     return;
+
+  // We want to honor the autoplay feature policy (via allow="autoplay") without
+  // explicit user activation, since media on Cast is extremely likely to have
+  // already been explicitly requested by a user via voice or over the network.
+  // By spoofing the "high media engagement" signal, we can bypass the user
+  // gesture requirement for autoplay.
+  int32_t autoplay_flags = blink::mojom::kAutoplayFlagHighMediaEngagement;
+
+  // Main frames should have autoplay enabled by default, since autoplay
+  // delegation via parent frame doesn't work here.
+  if (navigation_handle->IsInMainFrame())
+    autoplay_flags |= blink::mojom::kAutoplayFlagForceAllow;
+
+  mojo::AssociatedRemote<blink::mojom::AutoplayConfigurationClient> client;
+  navigation_handle->GetRenderFrameHost()
+      ->GetRemoteAssociatedInterfaces()
+      ->GetInterface(&client);
+  auto autoplay_origin = url::Origin::Create(navigation_handle->GetURL());
+  client->AddAutoplayFlags(autoplay_origin, autoplay_flags);
+
   if (!navigation_handle->IsInMainFrame())
     return;
 
diff --git a/chromeos/components/media_app_ui/resources/js/launch.js b/chromeos/components/media_app_ui/resources/js/launch.js
index 0015d55..356e142f 100644
--- a/chromeos/components/media_app_ui/resources/js/launch.js
+++ b/chromeos/components/media_app_ui/resources/js/launch.js
@@ -94,7 +94,7 @@
  * @param {string} fileName
  * @param {string} errorName
  * @param {!OverwriteFileMessage} overwrite
- * @return {!Promise<!OverwriteFileResponse>}
+ * @return {!Promise<!OverwriteViaFilePickerResponse>}
  */
 async function pickFileForFailedOverwrite(fileName, errorName, overwrite) {
   const fileHandle = await pickWritableFile(fileName, overwrite.blob.type);
diff --git a/chromeos/components/media_app_ui/resources/js/media_app.externs.js b/chromeos/components/media_app_ui/resources/js/media_app.externs.js
index f4b2d1b..1bdb833 100644
--- a/chromeos/components/media_app_ui/resources/js/media_app.externs.js
+++ b/chromeos/components/media_app_ui/resources/js/media_app.externs.js
@@ -9,15 +9,6 @@
  * TODO(b/142750452): Convert this file to ES6.
  */
 
-/**
- * Response message to a successful overwrite (no error thrown). If defined,
- * indicates that an overwrite failed, but the user was able to select a new
- * file from a file picker. The UI should update to reflect the new name.
- * `errorName` is the error on the write attempt that triggered the picker.
- * @typedef {{renamedTo: string, errorName: string}|undefined}
- */
-let OverwriteFileResponse;
-
 /** @const */
 const mediaApp = {};
 
@@ -66,7 +57,7 @@
  * rejects. Upon success, `size` will reflect the new file size.
  * If null, then in-place overwriting is not supported for this file.
  * Note the "overwrite" may be simulated with a download operation.
- * @type {function(!Blob): !Promise<!OverwriteFileResponse>}
+ * @type {function(!Blob): !Promise<undefined>|undefined}
  */
 mediaApp.AbstractFile.prototype.overwriteOriginal;
 /**
diff --git a/chromeos/components/media_app_ui/resources/js/message_types.js b/chromeos/components/media_app_ui/resources/js/message_types.js
index 35e6ba4..7b5b2b42 100644
--- a/chromeos/components/media_app_ui/resources/js/message_types.js
+++ b/chromeos/components/media_app_ui/resources/js/message_types.js
@@ -73,6 +73,15 @@
 let OverwriteFileMessage;
 
 /**
+ * Response message to a successful overwrite (no error thrown). If fields are
+ * defined, indicates that an overwrite failed, but the user was able to select
+ * a new file from a file picker. The UI should update to reflect the new name.
+ * `errorName` is the error on the write attempt that triggered the picker.
+ * @typedef {{renamedTo: (string|undefined), errorName: (string|undefined)}}
+ */
+let OverwriteViaFilePickerResponse;
+
+/**
  * Message sent by the unprivileged context to the privileged context requesting
  * the app be relaunched with the next/previous file in the current directory
  * set to writable. Direction must be either 'next' or 'prev'.
diff --git a/chromeos/components/media_app_ui/resources/js/receiver.js b/chromeos/components/media_app_ui/resources/js/receiver.js
index 54a3e9ff..6f81e02 100644
--- a/chromeos/components/media_app_ui/resources/js/receiver.js
+++ b/chromeos/components/media_app_ui/resources/js/receiver.js
@@ -36,14 +36,16 @@
     /** @type {!OverwriteFileMessage} */
     const message = {token: this.token, blob: blob};
 
-    const result = /** @type {!OverwriteFileResponse} */ (
+    const result = /** @type {!OverwriteViaFilePickerResponse} */ (
         await parentMessagePipe.sendMessage(Message.OVERWRITE_FILE, message));
-
     // Note the following are skipped if an exception is thrown above.
+    if (result.renamedTo) {
+      this.name = result.renamedTo;
+    }
+    this.error = result.errorName || '';
     this.blob = blob;
     this.size = blob.size;
     this.mimeType = blob.type;
-    return result;
   }
 
   /**
diff --git a/chromeos/components/media_app_ui/test/driver.js b/chromeos/components/media_app_ui/test/driver.js
index 29e15dce..d15c95a 100644
--- a/chromeos/components/media_app_ui/test/driver.js
+++ b/chromeos/components/media_app_ui/test/driver.js
@@ -407,19 +407,6 @@
 }
 
 /**
- * Use to match error stack traces.
- * @param {string} stackTrace the stacktrace
- * @param {!Array<string>} regexLines a list of escaped regex compatible
- *     strings, used to compare with the stacktrace.
- * @param {string=} opt_message logged if the assertion fails
- */
-function assertMatchErrorStack(
-    stackTrace, regexLines, opt_message = undefined) {
-  const regex = `(.|\\n)*${regexLines.join('(.|\\n)*')}(.|\\n)*`;
-  assertMatch(stackTrace, regex, opt_message);
-}
-
-/**
  * Returns the files loaded in the most recent call to `loadFiles()`.
  * @return {!Promise<?Array<!mediaApp.AbstractFile>>}
  */
diff --git a/chromeos/components/media_app_ui/test/guest_query_receiver.js b/chromeos/components/media_app_ui/test/guest_query_receiver.js
index 5ced8e6..c152e35 100644
--- a/chromeos/components/media_app_ui/test/guest_query_receiver.js
+++ b/chromeos/components/media_app_ui/test/guest_query_receiver.js
@@ -55,8 +55,12 @@
     }
   } else if (data.overwriteLastFile) {
     const testBlob = new Blob([data.overwriteLastFile]);
-    extraResultData = await assertCast(firstReceivedItem().overwriteOriginal)
-                          .call(firstReceivedItem(), testBlob);
+    const file = firstReceivedItem();
+    await assertCast(file.overwriteOriginal).call(file, testBlob);
+    extraResultData = {
+      receiverFileName: file.name,
+      receiverErrorName: file.error
+    };
     result = 'overwriteOriginal resolved';
   } else if (data.deleteLastFile) {
     try {
@@ -165,11 +169,6 @@
   // Turn off error rethrowing for tests so the test runner doesn't mark
   // our error handling tests as failed.
   parentMessagePipe.rethrowErrors = false;
-  // Handler that will always error for helping to test the message pipe
-  // itself.
-  parentMessagePipe.registerHandler('bad-handler', () => {
-    throw Error('This is an error');
-  });
 
   parentMessagePipe.registerHandler('run-test-case', (data) => {
     return runTestCase(/** @type{!TestMessageRunTestCase} */ (data));
diff --git a/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js
index ffabfcf..dd5e99a 100644
--- a/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js
+++ b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js
@@ -473,65 +473,6 @@
   testDone();
 });
 
-// Tests that we receive an error if our message is unhandled.
-TEST_F('MediaAppUIBrowserTest', 'ReceivesNoHandlerError', async () => {
-  guestMessagePipe.logClientError = error => console.log(JSON.stringify(error));
-  let caughtError = {};
-
-  try {
-    await guestMessagePipe.sendMessage('unknown-message');
-  } catch (error) {
-    caughtError = error;
-  }
-
-  assertEquals(caughtError.name, 'Error');
-  assertEquals(
-      caughtError.message,
-      'unknown-message: No handler registered for message type \'unknown-message\'');
-
-  assertMatchErrorStack(caughtError.stack, [
-    // Error stack of the test context.
-    'Error: unknown-message: No handler registered for message type \'unknown-message\'',
-    'at MessagePipe.sendMessage \\(chrome:',
-    'at async MediaAppUIBrowserTest.<anonymous>',
-    // Error stack of the untrusted context (guestMessagePipe) is appended.
-    'Error from chrome-untrusted:',
-    'Error: No handler registered for message type \'unknown-message\'',
-    'at MessagePipe.receiveMessage_ \\(chrome-untrusted:',
-    'at MessagePipe.messageListener_ \\(chrome-untrusted:',
-  ]);
-  testDone();
-});
-
-// Tests that we receive an error if the handler fails.
-TEST_F('MediaAppUIBrowserTest', 'ReceivesProxiedError', async () => {
-  guestMessagePipe.logClientError = error => console.log(JSON.stringify(error));
-  let caughtError = {};
-
-  try {
-    await guestMessagePipe.sendMessage('bad-handler');
-  } catch (error) {
-    caughtError = error;
-  }
-
-  assertEquals(caughtError.name, 'Error');
-  assertEquals(caughtError.message, 'bad-handler: This is an error');
-  assertMatchErrorStack(caughtError.stack, [
-    // Error stack of the test context.
-    'Error: bad-handler: This is an error',
-    'at MessagePipe.sendMessage \\(chrome:',
-    'at async MediaAppUIBrowserTest.<anonymous>',
-    // Error stack of the untrusted context (guestMessagePipe) is appended.
-    'Error from chrome-untrusted:',
-    'Error: This is an error',
-    'at guest_query_receiver.js',
-    'at MessagePipe.callHandlerForMessageType_ \\(chrome-untrusted:',
-    'at MessagePipe.receiveMessage_ \\(chrome-untrusted:',
-    'at MessagePipe.messageListener_ \\(chrome-untrusted:',
-  ]);
-  testDone();
-});
-
 // Tests the IPC behind the implementation of ReceivedFile.overwriteOriginal()
 // in the untrusted context. Ensures it correctly updates the file handle owned
 // by the privileged context.
@@ -547,6 +488,9 @@
   const writeResult = await handle.lastWritable.closePromise;
 
   assertEquals(testResponse.testQueryResult, 'overwriteOriginal resolved');
+  assertEquals(
+      testResponse.testQueryResultData['receiverFileName'], 'test_file.png');
+  assertEquals(testResponse.testQueryResultData['receiverErrorName'], '');
   assertEquals(await writeResult.text(), 'Foo');
   assertEquals(handle.lastWritable.writes.length, 1);
   assertDeepEquals(
@@ -570,8 +514,10 @@
   const writeResult = await pickedFile.lastWritable.closePromise;
 
   assertEquals(testResponse.testQueryResult, 'overwriteOriginal resolved');
-  assertEquals(testResponse.testQueryResultData['renamedTo'], 'pickme.png');
-  assertEquals(testResponse.testQueryResultData['errorName'], 'FakeError');
+  assertEquals(
+      testResponse.testQueryResultData['receiverFileName'], 'pickme.png');
+  assertEquals(
+      testResponse.testQueryResultData['receiverErrorName'], 'FakeError');
   assertEquals(await writeResult.text(), 'Foo');
   assertEquals(pickedFile.lastWritable.writes.length, 1);
   assertDeepEquals(
@@ -579,8 +525,7 @@
   testDone();
 });
 
-// Tests `MessagePipe.sendMessage()` properly propagates errors and appends
-// stacktraces.
+// Tests `MessagePipe.sendMessage()` properly propagates errors.
 TEST_F('MediaAppUIBrowserTest', 'CrossContextErrors', async () => {
   // Prevent the trusted context throwing errors resulting JS errors.
   guestMessagePipe.logClientError = error => console.log(JSON.stringify(error));
@@ -611,17 +556,6 @@
 
   assertEquals(caughtError.name, 'NotAllowedError');
   assertEquals(caughtError.message, `test: overwrite-file: ${error.message}`);
-  assertMatchErrorStack(caughtError.stack, [
-    // Error stack of the untrusted & test context.
-    'at MessagePipe.sendMessage \\(chrome-untrusted:',
-    'at async ReceivedFile.overwriteOriginal \\(chrome-untrusted:',
-    'at async runTestQuery \\(guest_query_receiver',
-    'at async MessagePipe.callHandlerForMessageType_ \\(chrome-untrusted:',
-    // Error stack of the trusted context is appended.
-    'Error from chrome:',
-    'NotAllowedError: Fake NotAllowedError for CrossContextErrors test.',
-    'at MediaAppUIBrowserTest',
-  ]);
   testDone();
 });
 
diff --git a/chromeos/components/print_management/resources/print_management.html b/chromeos/components/print_management/resources/print_management.html
index d22a4f8..7213c36 100644
--- a/chromeos/components/print_management/resources/print_management.html
+++ b/chromeos/components/print_management/resources/print_management.html
@@ -31,6 +31,8 @@
   }
 
   #deleteIcon {
+    --iron-icon-height: 20px;
+    --iron-icon-width: 20px;
     margin-inline-end: 4px;
   }
 
diff --git a/chromeos/components/system_apps/public/js/BUILD.gn b/chromeos/components/system_apps/public/js/BUILD.gn
index d64b337..321f0c3 100644
--- a/chromeos/components/system_apps/public/js/BUILD.gn
+++ b/chromeos/components/system_apps/public/js/BUILD.gn
@@ -16,23 +16,14 @@
 
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
-  deps = [ ":browser_test_support" ]
+  deps = [ "//chromeos/components/web_applications/test:test_support" ]
 
-  data = [ "message_pipe_test.html" ]
-}
-
-source_set("browser_test_support") {
-  testonly = true
-  sources = [
-    "message_pipe_browsertest.cc",
-    "message_pipe_browsertest.h",
-  ]
-
-  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-
-  deps = [
-    "//chrome/test:test_support_ui",
-    "//chromeos/components/web_applications/test:test_support",
+  data = [
+    "message_pipe.js",
+    "test_data/message_pipe_browsertest_trusted.html",
+    "test_data/message_pipe_browsertest_trusted.js",
+    "test_data/message_pipe_browsertest_untrusted.html",
+    "test_data/message_pipe_browsertest_untrusted.js",
   ]
 }
 
@@ -57,7 +48,11 @@
 
 js_library("message_pipe_browsertest_js") {
   testonly = true
-  sources = [ "message_pipe_browsertest.js" ]
+  sources = [
+    "message_pipe_browsertest.js",
+    "test_data/message_pipe_browsertest_trusted.js",
+    "test_data/message_pipe_browsertest_untrusted.js",
+  ]
   externs_list = [
     "//chromeos/components/web_applications/js2gtest_support.externs.js",
     "//third_party/chaijs/externs/chai-3.5.js",
diff --git a/chromeos/components/system_apps/public/js/message_pipe_browsertest.cc b/chromeos/components/system_apps/public/js/message_pipe_browsertest.cc
deleted file mode 100644
index 282735e43..0000000
--- a/chromeos/components/system_apps/public/js/message_pipe_browsertest.cc
+++ /dev/null
@@ -1,19 +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 "chromeos/components/system_apps/public/js/message_pipe_browsertest.h"
-
-#include "base/files/file_path.h"
-
-namespace {
-
-constexpr base::FilePath::CharType kRootDir[] =
-    FILE_PATH_LITERAL("chromeos/components/system_apps/public/js/");
-
-}  // namespace
-
-MessagePipeBrowserTestBase::MessagePipeBrowserTestBase()
-    : JsLibraryTest(base::FilePath(kRootDir)) {}
-
-MessagePipeBrowserTestBase::~MessagePipeBrowserTestBase() = default;
diff --git a/chromeos/components/system_apps/public/js/message_pipe_browsertest.h b/chromeos/components/system_apps/public/js/message_pipe_browsertest.h
deleted file mode 100644
index 737d706..0000000
--- a/chromeos/components/system_apps/public/js/message_pipe_browsertest.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_COMPONENTS_SYSTEM_APPS_PUBLIC_JS_MESSAGE_PIPE_BROWSERTEST_H_
-#define CHROMEOS_COMPONENTS_SYSTEM_APPS_PUBLIC_JS_MESSAGE_PIPE_BROWSERTEST_H_
-
-#include "chromeos/components/web_applications/test/js_library_test.h"
-
-class MessagePipeBrowserTestBase : public JsLibraryTest {
- public:
-  MessagePipeBrowserTestBase();
-  ~MessagePipeBrowserTestBase() override;
-
-  MessagePipeBrowserTestBase(const MessagePipeBrowserTestBase&) = delete;
-  MessagePipeBrowserTestBase& operator=(const MessagePipeBrowserTestBase&) =
-      delete;
-};
-
-#endif  // CHROMEOS_COMPONENTS_SYSTEM_APPS_PUBLIC_JS_MESSAGE_PIPE_BROWSERTEST_H_
diff --git a/chromeos/components/system_apps/public/js/message_pipe_browsertest.js b/chromeos/components/system_apps/public/js/message_pipe_browsertest.js
index 34363f80b..ae662ce5 100644
--- a/chromeos/components/system_apps/public/js/message_pipe_browsertest.js
+++ b/chromeos/components/system_apps/public/js/message_pipe_browsertest.js
@@ -3,16 +3,49 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview Test suite for message_pipe.js
+ * @fileoverview Test suite for message_pipe.js.
  */
+GEN('#include "chromeos/components/web_applications/test/js_library_test.h"');
 
-GEN('#include "chromeos/components/system_apps/public/js/message_pipe_browsertest.h"');
 GEN('#include "content/public/test/browser_test.h"');
 
+/**
+ * Wraps `chai.assert.match` allowing tests to use `assertMatch`.
+ * @param {string} string the string to match
+ * @param {string} regex an escaped regex compatible string
+ * @param {string=} opt_message logged if the assertion fails
+ */
+function assertMatch(string, regex, opt_message = undefined) {
+  chai.assert.match(string, new RegExp(regex), opt_message);
+}
+
+/**
+ * Use to match error stack traces.
+ * @param {string} stackTrace the stacktrace
+ * @param {!Array<string>} regexLines a list of escaped regex compatible
+ *     strings, used to compare with the stacktrace.
+ * @param {string=} opt_message logged if the assertion fails
+ */
+function assertMatchErrorStack(
+    stackTrace, regexLines, opt_message = undefined) {
+  const regex = `(.|\\n)*${regexLines.join('(.|\\n)*')}(.|\\n)*`;
+  assertMatch(stackTrace, regex, opt_message);
+}
+
+/**
+ * @param {string} messageType
+ * @param {!Object=} message
+ * @return {!Promise<!Object>}
+ */
+async function sendTestMessage(messageType, message = {}) {
+  await testMessageHandlersReady;
+  return untrustedMessagePipe.sendMessage(messageType, message);
+}
+
 var MessagePipeBrowserTest = class extends testing.Test {
   /** @override */
   get browsePreload() {
-    return 'chrome://system-app-test/message_pipe_test.html';
+    return 'chrome://system-app-test/test_data/message_pipe_browsertest_trusted.html';
   }
 
   /** @override */
@@ -22,7 +55,7 @@
 
   /** @override */
   get typedefCppFixture() {
-    return 'MessagePipeBrowserTestBase';
+    return 'JsLibraryTest';
   }
 
   /** @override */
@@ -31,6 +64,112 @@
   }
 };
 
-TEST_F('MessagePipeBrowserTest', 'Empty', () => {
+TEST_F('MessagePipeBrowserTest', 'ReceivesSuccessResponse', async () => {
+  const request = {'foo': 'bar'};
+  const response = await sendTestMessage('success-message', request);
+  assertDeepEquals(response, {'success': true, 'request': request});
+  testDone();
+});
+
+// Tests that we receive an error if our message is unhandled.
+TEST_F('MessagePipeBrowserTest', 'ReceivesNoHandlerError', async () => {
+  untrustedMessagePipe.logClientError = error =>
+      console.log(JSON.stringify(error));
+  let caughtError = {};
+
+  try {
+    await sendTestMessage('unknown-message');
+  } catch (error) {
+    caughtError = error;
+  }
+
+  assertEquals(caughtError.name, 'Error');
+  assertEquals(
+      caughtError.message,
+      'unknown-message: No handler registered for message type \'unknown-message\'');
+  assertMatchErrorStack(caughtError.stack, [
+    // Error stack of the test context.
+    'Error: unknown-message: No handler registered for message type \'unknown-message\'',
+    'at MessagePipe.sendMessage \\(chrome://system-app-test/',
+    'at async MessagePipeBrowserTest.',
+    // Error stack of the untrusted context.
+    'Error from chrome-untrusted://system-app-test',
+    'Error: No handler registered for message type \'unknown-message\'',
+    'at MessagePipe.receiveMessage_ \\(chrome-untrusted://system-app-test/',
+    'at MessagePipe.messageListener_ \\(chrome-untrusted://system-app-test/'
+  ]);
+  testDone();
+});
+
+// Tests that we receive an error if the handler fails.
+TEST_F('MessagePipeBrowserTest', 'ReceivesProxiedError', async () => {
+  untrustedMessagePipe.logClientError = error =>
+      console.log(JSON.stringify(error));
+  let caughtError = {};
+
+  try {
+    await sendTestMessage('bad-handler');
+  } catch (error) {
+    caughtError = error;
+  }
+
+  assertEquals(caughtError.name, 'Error');
+  assertEquals(
+      caughtError.message, 'bad-handler: This is an error from untrusted');
+  assertMatchErrorStack(caughtError.stack, [
+    // Error stack of the test context.
+    'Error: bad-handler: This is an error from untrusted',
+    'at MessagePipe.sendMessage \\(chrome://system-app-test/',
+    'at async MessagePipeBrowserTest.',
+    // Error stack of the untrusted context.
+    'Error from chrome-untrusted://system-app-test',
+    'Error: This is an error from untrusted',
+    'at chrome-untrusted://system-app-test/test_data/message_pipe_browsertest_untrusted.js',
+    'at MessagePipe.callHandlerForMessageType_ \\(chrome-untrusted://system-app-test/',
+    'at MessagePipe.receiveMessage_ \\(chrome-untrusted://system-app-test/',
+    'at MessagePipe.messageListener_ \\(chrome-untrusted://system-app-test/'
+  ]);
+  testDone();
+});
+
+// Tests `MessagePipe.sendMessage()` properly propagates errors and appends
+// stacktraces.
+TEST_F('MessagePipeBrowserTest', 'CrossContextErrors', async () => {
+  untrustedMessagePipe.logClientError = error =>
+      console.log(JSON.stringify(error));
+  untrustedMessagePipe.rethrowErrors = false;
+
+  untrustedMessagePipe.registerHandler('bad-handler', () => {
+    throw Error('This is an error from trusted');
+  });
+
+  let caughtError = {};
+
+  try {
+    await sendTestMessage('request-bad-handler');
+  } catch (e) {
+    caughtError = e;
+  }
+
+  assertEquals(caughtError.name, 'Error');
+  assertEquals(
+      caughtError.message,
+      'request-bad-handler: bad-handler: This is an error from trusted');
+  assertMatchErrorStack(caughtError.stack, [
+    // Error stack of the test context.
+    'Error: request-bad-handler: bad-handler: This is an error from trusted',
+    'at MessagePipe.sendMessage \\(chrome://system-app-test/',
+    'at async MessagePipeBrowserTest',
+    // Error stack of the untrusted context.
+    'Error from chrome-untrusted://system-app-test',
+    'Error: bad-handler: This is an error from trusted',
+    'at MessagePipe.sendMessage \\(chrome-untrusted://system-app-test/',
+    'at async MessagePipe.callHandlerForMessageType_ \\(chrome-untrusted://system-app-test/',
+    // Error stack of the trusted context.
+    'Error from chrome://system-app-test',
+    'Error: This is an error from trusted', 'at .*message_pipe_browsertest.js',
+    'at MessagePipe.callHandlerForMessageType_',
+    'at MessagePipe.receiveMessage_', 'at MessagePipe.messageListener_'
+  ]);
   testDone();
 });
diff --git a/chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_trusted.html b/chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_trusted.html
new file mode 100644
index 0000000..38955a6
--- /dev/null
+++ b/chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_trusted.html
@@ -0,0 +1,11 @@
+<!-- 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. -->
+<!DOCTYPE html>
+
+<script src="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js"></script>
+
+<iframe src="chrome-untrusted://system-app-test/test_data/message_pipe_browsertest_untrusted.html"></iframe>
+
+<script src="/message_pipe.js"></script>
+<script src="/test_data/message_pipe_browsertest_trusted.js"></script>
diff --git a/chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_trusted.js b/chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_trusted.js
new file mode 100644
index 0000000..fb61bb3
--- /dev/null
+++ b/chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_trusted.js
@@ -0,0 +1,17 @@
+// 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.
+
+/** A pipe through which we can send messages to the untrusted frame. */
+const untrustedMessagePipe =
+    new MessagePipe('chrome-untrusted://system-app-test');
+
+/**
+ * Promise that signals the guest is ready to receive test messages.
+ * @type {!Promise<undefined>}
+ */
+const testMessageHandlersReady = new Promise(resolve => {
+  window.addEventListener('DOMContentLoaded', () => {
+    untrustedMessagePipe.registerHandler('test-handlers-ready', resolve);
+  });
+});
diff --git a/chromeos/components/system_apps/public/js/message_pipe_test.html b/chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_untrusted.html
similarity index 62%
rename from chromeos/components/system_apps/public/js/message_pipe_test.html
rename to chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_untrusted.html
index 45328c0..3be84a6 100644
--- a/chromeos/components/system_apps/public/js/message_pipe_test.html
+++ b/chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_untrusted.html
@@ -1,7 +1,7 @@
 <!-- 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. -->
-
 <!DOCTYPE html>
 
-<script src="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js"></script>
+<script src="/message_pipe.js"></script>
+<script src="/test_data/message_pipe_browsertest_untrusted.js"></script>
diff --git a/chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_untrusted.js b/chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_untrusted.js
new file mode 100644
index 0000000..3a9f30bb
--- /dev/null
+++ b/chromeos/components/system_apps/public/js/test_data/message_pipe_browsertest_untrusted.js
@@ -0,0 +1,63 @@
+// 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.
+
+/** A pipe through which we can send messages to the parent frame. */
+const parentMessagePipe =
+    new MessagePipe('chrome://system-app-test', window.parent);
+
+/**
+ * Tells the test driver the guest test message handlers are installed. This
+ * requires the test handler that receives the signal to be set up. The order
+ * that this occurs can not be guaranteed. So this function retries until the
+ * signal is handled, which requires the 'test-handlers-ready' handler to be
+ * registered in message_pipe_browsertest_trusted.js.
+ */
+async function signalTestHandlersReady() {
+  const EXPECTED_ERROR =
+      `No handler registered for message type 'test-handlers-ready'`;
+  while (true) {
+    try {
+      await parentMessagePipe.sendMessage('test-handlers-ready', {});
+      return;
+    } catch (/** @type {!GenericErrorResponse} */ e) {
+      if (e.message !== EXPECTED_ERROR) {
+        console.error('Unexpected error in signalTestHandlersReady', e);
+        return;
+      }
+    }
+  }
+}
+
+/** Installs the MessagePipe handlers for receiving test queries. */
+function installTestHandlers() {
+  // Turn off error rethrowing for tests so the test runner doesn't mark
+  // our error handling tests as failed.
+  parentMessagePipe.rethrowErrors = false;
+
+  // Log errors, rather than send them to console.error. This allows the error
+  // handling tests to work correctly.
+  parentMessagePipe.logClientError = error =>
+      console.log(JSON.stringify(error));
+
+  parentMessagePipe.registerHandler('success-message', (message) => {
+    return {'success': true, 'request': message};
+  });
+
+  parentMessagePipe.registerHandler('bad-handler', () => {
+    throw Error('This is an error from untrusted');
+  });
+
+  parentMessagePipe.registerHandler('request-bad-handler', async () => {
+    return parentMessagePipe.sendMessage('bad-handler');
+  });
+
+  signalTestHandlersReady();
+}
+
+// Ensure content and all scripts have loaded before installing test handlers.
+if (document.readyState !== 'complete') {
+  window.addEventListener('load', installTestHandlers);
+} else {
+  installTestHandlers();
+}
diff --git a/chromeos/components/tether/fake_tether_connector.cc b/chromeos/components/tether/fake_tether_connector.cc
index 20bbfb09..3825b838 100644
--- a/chromeos/components/tether/fake_tether_connector.cc
+++ b/chromeos/components/tether/fake_tether_connector.cc
@@ -15,13 +15,13 @@
 void FakeTetherConnector::ConnectToNetwork(
     const std::string& tether_network_guid,
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback) {
+    StringErrorCallback error_callback) {
   last_connected_tether_network_guid_ = tether_network_guid;
 
   if (connection_error_name_.empty())
     std::move(success_callback).Run();
   else
-    error_callback.Run(connection_error_name_);
+    std::move(error_callback).Run(connection_error_name_);
 }
 
 bool FakeTetherConnector::CancelConnectionAttempt(
diff --git a/chromeos/components/tether/fake_tether_connector.h b/chromeos/components/tether/fake_tether_connector.h
index f812d61..935f905b 100644
--- a/chromeos/components/tether/fake_tether_connector.h
+++ b/chromeos/components/tether/fake_tether_connector.h
@@ -37,10 +37,9 @@
   }
 
   // TetherConnector:
-  void ConnectToNetwork(
-      const std::string& tether_network_guid,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) override;
+  void ConnectToNetwork(const std::string& tether_network_guid,
+                        base::OnceClosure success_callback,
+                        StringErrorCallback error_callback) override;
   bool CancelConnectionAttempt(const std::string& tether_network_guid) override;
 
  private:
diff --git a/chromeos/components/tether/fake_tether_disconnector.cc b/chromeos/components/tether/fake_tether_disconnector.cc
index 36258d9..e1a4d82 100644
--- a/chromeos/components/tether/fake_tether_disconnector.cc
+++ b/chromeos/components/tether/fake_tether_disconnector.cc
@@ -15,7 +15,7 @@
 void FakeTetherDisconnector::DisconnectFromNetwork(
     const std::string& tether_network_guid,
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback,
+    StringErrorCallback error_callback,
     const TetherSessionCompletionLogger::SessionCompletionReason&
         session_completion_reason) {
   last_disconnected_tether_network_guid_ = tether_network_guid;
@@ -26,7 +26,7 @@
   if (disconnection_error_name_.empty())
     std::move(success_callback).Run();
   else
-    error_callback.Run(disconnection_error_name_);
+    std::move(error_callback).Run(disconnection_error_name_);
 }
 
 }  // namespace tether
diff --git a/chromeos/components/tether/fake_tether_disconnector.h b/chromeos/components/tether/fake_tether_disconnector.h
index ab803ac..d0752a45 100644
--- a/chromeos/components/tether/fake_tether_disconnector.h
+++ b/chromeos/components/tether/fake_tether_disconnector.h
@@ -39,7 +39,7 @@
   void DisconnectFromNetwork(
       const std::string& tether_network_guid,
       base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback,
+      StringErrorCallback error_callback,
       const TetherSessionCompletionLogger::SessionCompletionReason&
           session_completion_reason) override;
 
diff --git a/chromeos/components/tether/fake_wifi_hotspot_disconnector.cc b/chromeos/components/tether/fake_wifi_hotspot_disconnector.cc
index 5c19229..ab9d39c 100644
--- a/chromeos/components/tether/fake_wifi_hotspot_disconnector.cc
+++ b/chromeos/components/tether/fake_wifi_hotspot_disconnector.cc
@@ -15,13 +15,13 @@
 void FakeWifiHotspotDisconnector::DisconnectFromWifiHotspot(
     const std::string& wifi_network_guid,
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback) {
+    StringErrorCallback error_callback) {
   last_disconnected_wifi_network_guid_ = wifi_network_guid;
 
   if (disconnection_error_name_.empty())
     std::move(success_callback).Run();
   else
-    error_callback.Run(disconnection_error_name_);
+    std::move(error_callback).Run(disconnection_error_name_);
 }
 
 }  // namespace tether
diff --git a/chromeos/components/tether/fake_wifi_hotspot_disconnector.h b/chromeos/components/tether/fake_wifi_hotspot_disconnector.h
index 165250a..2d693ad 100644
--- a/chromeos/components/tether/fake_wifi_hotspot_disconnector.h
+++ b/chromeos/components/tether/fake_wifi_hotspot_disconnector.h
@@ -29,10 +29,9 @@
   }
 
   // WifiHotspotDisconnector:
-  void DisconnectFromWifiHotspot(
-      const std::string& wifi_network_guid,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) override;
+  void DisconnectFromWifiHotspot(const std::string& wifi_network_guid,
+                                 base::OnceClosure success_callback,
+                                 StringErrorCallback error_callback) override;
 
  private:
   std::string last_disconnected_wifi_network_guid_;
diff --git a/chromeos/components/tether/network_connection_handler_tether_delegate.cc b/chromeos/components/tether/network_connection_handler_tether_delegate.cc
index 9cb7fc7..41e4f12 100644
--- a/chromeos/components/tether/network_connection_handler_tether_delegate.cc
+++ b/chromeos/components/tether/network_connection_handler_tether_delegate.cc
@@ -29,9 +29,9 @@
 
 NetworkConnectionHandlerTetherDelegate::Callbacks::Callbacks(
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback)
+    StringErrorCallback error_callback)
     : success_callback(std::move(success_callback)),
-      error_callback(error_callback) {}
+      error_callback(std::move(error_callback)) {}
 
 NetworkConnectionHandlerTetherDelegate::Callbacks::Callbacks(Callbacks&&) =
     default;
@@ -54,9 +54,9 @@
     ~NetworkConnectionHandlerTetherDelegate() {
   // If there are still pending callbacks, invoke them here. It should never be
   // possible for the Tether component to shut down with pending callbacks.
-  for (const auto& entry : request_num_to_callbacks_map_) {
-    entry.second.error_callback.Run(
-        NetworkConnectionHandler::kErrorConnectFailed);
+  for (auto& entry : request_num_to_callbacks_map_) {
+    std::move(entry.second.error_callback)
+        .Run(NetworkConnectionHandler::kErrorConnectFailed);
   }
 
   network_connection_handler_->SetTetherDelegate(nullptr);
@@ -65,10 +65,11 @@
 void NetworkConnectionHandlerTetherDelegate::DisconnectFromNetwork(
     const std::string& tether_network_guid,
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback) {
+    StringErrorCallback error_callback) {
   int request_num = next_request_num_++;
   request_num_to_callbacks_map_.emplace(
-      request_num, Callbacks(std::move(success_callback), error_callback));
+      request_num,
+      Callbacks(std::move(success_callback), std::move(error_callback)));
   tether_disconnector_->DisconnectFromNetwork(
       tether_network_guid,
       base::BindOnce(&NetworkConnectionHandlerTetherDelegate::OnRequestSuccess,
@@ -82,14 +83,14 @@
 void NetworkConnectionHandlerTetherDelegate::ConnectToNetwork(
     const std::string& tether_network_guid,
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback) {
+    StringErrorCallback error_callback) {
   if (active_host_->GetActiveHostStatus() ==
       ActiveHost::ActiveHostStatus::CONNECTED) {
     if (active_host_->GetTetherNetworkGuid() == tether_network_guid) {
       PA_LOG(WARNING) << "Received a request to connect to Tether network with "
                       << "GUID " << tether_network_guid << ", but that network "
                       << "is already connected. Ignoring this request.";
-      error_callback.Run(NetworkConnectionHandler::kErrorConnected);
+      std::move(error_callback).Run(NetworkConnectionHandler::kErrorConnected);
       return;
     }
 
@@ -107,7 +108,8 @@
 
   int request_num = next_request_num_++;
   request_num_to_callbacks_map_.emplace(
-      request_num, Callbacks(std::move(success_callback), error_callback));
+      request_num,
+      Callbacks(std::move(success_callback), std::move(error_callback)));
   tether_connector_->ConnectToNetwork(
       tether_network_guid,
       base::BindOnce(&NetworkConnectionHandlerTetherDelegate::OnRequestSuccess,
@@ -127,7 +129,8 @@
     int request_num,
     const std::string& error_name) {
   DCHECK(base::Contains(request_num_to_callbacks_map_, request_num));
-  request_num_to_callbacks_map_.at(request_num).error_callback.Run(error_name);
+  std::move(request_num_to_callbacks_map_.at(request_num).error_callback)
+      .Run(error_name);
   request_num_to_callbacks_map_.erase(request_num);
 }
 
diff --git a/chromeos/components/tether/network_connection_handler_tether_delegate.h b/chromeos/components/tether/network_connection_handler_tether_delegate.h
index a669c5ef..16f4758 100644
--- a/chromeos/components/tether/network_connection_handler_tether_delegate.h
+++ b/chromeos/components/tether/network_connection_handler_tether_delegate.h
@@ -10,7 +10,6 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/network/network_connection_handler.h"
-#include "chromeos/network/network_handler_callbacks.h"
 
 namespace chromeos {
 
@@ -34,25 +33,23 @@
   ~NetworkConnectionHandlerTetherDelegate() override;
 
   // NetworkConnectionHandler::TetherDelegate:
-  void DisconnectFromNetwork(
-      const std::string& tether_network_guid,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) override;
-  void ConnectToNetwork(
-      const std::string& tether_network_guid,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) override;
+  void DisconnectFromNetwork(const std::string& tether_network_guid,
+                             base::OnceClosure success_callback,
+                             StringErrorCallback error_callback) override;
+  void ConnectToNetwork(const std::string& tether_network_guid,
+                        base::OnceClosure success_callback,
+                        StringErrorCallback error_callback) override;
 
  private:
   struct Callbacks {
    public:
     Callbacks(base::OnceClosure success_callback,
-              const network_handler::StringResultCallback& error_callback);
+              StringErrorCallback error_callback);
     Callbacks(Callbacks&&);
     ~Callbacks();
 
     base::OnceClosure success_callback;
-    network_handler::StringResultCallback error_callback;
+    StringErrorCallback error_callback;
   };
 
   void OnRequestSuccess(int request_num);
diff --git a/chromeos/components/tether/network_connection_handler_tether_delegate_unittest.cc b/chromeos/components/tether/network_connection_handler_tether_delegate_unittest.cc
index 8a25618..edd3044 100644
--- a/chromeos/components/tether/network_connection_handler_tether_delegate_unittest.cc
+++ b/chromeos/components/tether/network_connection_handler_tether_delegate_unittest.cc
@@ -28,10 +28,9 @@
 class DummyTetherConnector : public FakeTetherConnector {
  public:
   // TetherConnector:
-  void ConnectToNetwork(
-      const std::string& tether_network_guid,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) override {}
+  void ConnectToNetwork(const std::string& tether_network_guid,
+                        base::OnceClosure success_callback,
+                        StringErrorCallback error_callback) override {}
 };
 
 // Does nothing when a disconnection is requested.
@@ -41,7 +40,7 @@
   void DisconnectFromNetwork(
       const std::string& tether_network_guid,
       base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback,
+      StringErrorCallback error_callback,
       const TetherSessionCompletionLogger::SessionCompletionReason&
           session_completion_reason) override {}
 };
diff --git a/chromeos/components/tether/tether_connector.h b/chromeos/components/tether/tether_connector.h
index b9c4eae..52c9efa 100644
--- a/chromeos/components/tether/tether_connector.h
+++ b/chromeos/components/tether/tether_connector.h
@@ -20,13 +20,15 @@
 // the associated Wi-Fi network.
 class TetherConnector {
  public:
+  using StringErrorCallback =
+      NetworkConnectionHandler::TetherDelegate::StringErrorCallback;
+
   TetherConnector() {}
   virtual ~TetherConnector() {}
 
-  virtual void ConnectToNetwork(
-      const std::string& tether_network_guid,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) = 0;
+  virtual void ConnectToNetwork(const std::string& tether_network_guid,
+                                base::OnceClosure success_callback,
+                                StringErrorCallback error_callback) = 0;
 
   // Returns whether the connection attempt was successfully canceled.
   virtual bool CancelConnectionAttempt(
diff --git a/chromeos/components/tether/tether_connector_impl.cc b/chromeos/components/tether/tether_connector_impl.cc
index 9453488..b2834ce 100644
--- a/chromeos/components/tether/tether_connector_impl.cc
+++ b/chromeos/components/tether/tether_connector_impl.cc
@@ -73,7 +73,7 @@
 void TetherConnectorImpl::ConnectToNetwork(
     const std::string& tether_network_guid,
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback) {
+    StringErrorCallback error_callback) {
   DCHECK(!tether_network_guid.empty());
   DCHECK(!success_callback.is_null());
   DCHECK(!error_callback.is_null());
@@ -104,7 +104,7 @@
 
   device_id_pending_connection_ = device_id;
   success_callback_ = std::move(success_callback);
-  error_callback_ = error_callback;
+  error_callback_ = std::move(error_callback);
   active_host_->SetActiveHostConnecting(device_id, tether_network_guid);
 
   tether_host_fetcher_->FetchTetherHost(
@@ -276,7 +276,7 @@
   notification_presenter_->RemoveSetupRequiredNotification();
 
   // Save a copy of the callback before resetting it below.
-  network_handler::StringResultCallback error_callback = error_callback_;
+  StringErrorCallback error_callback = std::move(error_callback_);
 
   std::string failed_connection_device_id = device_id_pending_connection_;
   device_id_pending_connection_.clear();
@@ -284,7 +284,7 @@
   success_callback_.Reset();
   error_callback_.Reset();
 
-  error_callback.Run(error_name);
+  std::move(error_callback).Run(error_name);
   active_host_->SetActiveHostDisconnected();
 
   host_connection_metrics_logger_->RecordConnectionToHostResult(
diff --git a/chromeos/components/tether/tether_connector_impl.h b/chromeos/components/tether/tether_connector_impl.h
index 616636ab..c20d621 100644
--- a/chromeos/components/tether/tether_connector_impl.h
+++ b/chromeos/components/tether/tether_connector_impl.h
@@ -61,10 +61,9 @@
       WifiHotspotDisconnector* wifi_hotspot_disconnector);
   ~TetherConnectorImpl() override;
 
-  void ConnectToNetwork(
-      const std::string& tether_network_guid,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) override;
+  void ConnectToNetwork(const std::string& tether_network_guid,
+                        base::OnceClosure success_callback,
+                        StringErrorCallback error_callback) override;
 
   // Returns whether the connection attempt was successfully canceled.
   bool CancelConnectionAttempt(const std::string& tether_network_guid) override;
@@ -117,7 +116,7 @@
   bool did_send_successful_request_ = false;
   std::string device_id_pending_connection_;
   base::OnceClosure success_callback_;
-  network_handler::StringResultCallback error_callback_;
+  StringErrorCallback error_callback_;
   std::unique_ptr<ConnectTetheringOperation> connect_tethering_operation_;
   base::Time connect_to_host_start_time_;
   base::WeakPtrFactory<TetherConnectorImpl> weak_ptr_factory_{this};
diff --git a/chromeos/components/tether/tether_disconnector.h b/chromeos/components/tether/tether_disconnector.h
index 1305850..9de63d5 100644
--- a/chromeos/components/tether/tether_disconnector.h
+++ b/chromeos/components/tether/tether_disconnector.h
@@ -12,7 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chromeos/components/tether/disconnect_tethering_operation.h"
 #include "chromeos/components/tether/tether_session_completion_logger.h"
-#include "chromeos/network/network_handler_callbacks.h"
+#include "chromeos/network/network_connection_handler.h"
 
 namespace chromeos {
 
@@ -21,6 +21,9 @@
 // Disconnects from an active Tether connection.
 class TetherDisconnector {
  public:
+  using StringErrorCallback =
+      NetworkConnectionHandler::TetherDelegate::StringErrorCallback;
+
   TetherDisconnector() {}
   virtual ~TetherDisconnector() {}
 
@@ -31,7 +34,7 @@
   virtual void DisconnectFromNetwork(
       const std::string& tether_network_guid,
       base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback,
+      StringErrorCallback error_callback,
       const TetherSessionCompletionLogger::SessionCompletionReason&
           session_completion_reason) = 0;
 
diff --git a/chromeos/components/tether/tether_disconnector_impl.cc b/chromeos/components/tether/tether_disconnector_impl.cc
index b15d6713..ef7efc6d 100644
--- a/chromeos/components/tether/tether_disconnector_impl.cc
+++ b/chromeos/components/tether/tether_disconnector_impl.cc
@@ -40,7 +40,7 @@
 void TetherDisconnectorImpl::DisconnectFromNetwork(
     const std::string& tether_network_guid,
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback,
+    StringErrorCallback error_callback,
     const TetherSessionCompletionLogger::SessionCompletionReason&
         session_completion_reason) {
   DCHECK(!tether_network_guid.empty());
@@ -52,7 +52,7 @@
   if (status == ActiveHost::ActiveHostStatus::DISCONNECTED) {
     PA_LOG(ERROR) << "Disconnect requested for Tether network with GUID "
                   << tether_network_guid << ", but no device is connected.";
-    error_callback.Run(NetworkConnectionHandler::kErrorNotConnected);
+    std::move(error_callback).Run(NetworkConnectionHandler::kErrorNotConnected);
     return;
   }
 
@@ -60,7 +60,7 @@
     PA_LOG(ERROR) << "Disconnect requested for Tether network with GUID "
                   << tether_network_guid << ", but that device is not the "
                   << "active host.";
-    error_callback.Run(NetworkConnectionHandler::kErrorNotConnected);
+    std::move(error_callback).Run(NetworkConnectionHandler::kErrorNotConnected);
     return;
   }
 
@@ -78,13 +78,15 @@
     PA_LOG(ERROR) << "Disconnect requested for Tether network with GUID "
                   << tether_network_guid << " (not yet connected), but "
                   << "canceling connection attempt failed.";
-    error_callback.Run(NetworkConnectionHandler::kErrorDisconnectFailed);
+    std::move(error_callback)
+        .Run(NetworkConnectionHandler::kErrorDisconnectFailed);
     return;
   }
 
   DCHECK(!active_wifi_network_guid.empty());
   DisconnectActiveWifiConnection(tether_network_guid, active_wifi_network_guid,
-                                 std::move(success_callback), error_callback);
+                                 std::move(success_callback),
+                                 std::move(error_callback));
 
   tether_session_completion_logger_->RecordTetherSessionCompletion(
       session_completion_reason);
@@ -94,7 +96,7 @@
     const std::string& tether_network_guid,
     const std::string& wifi_network_guid,
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback) {
+    StringErrorCallback error_callback) {
   // First, disconnect the active host so that the user gets visual indication
   // that the disconnection is in progress as quickly as possible.
   active_host_->SetActiveHostDisconnected();
@@ -102,7 +104,8 @@
   // Disconnect from the Wi-Fi hotspot. This transfers responsibility for
   // invoking the success or error callbacks to |wifi_hotspot_disconnector_|.
   wifi_hotspot_disconnector_->DisconnectFromWifiHotspot(
-      wifi_network_guid, std::move(success_callback), error_callback);
+      wifi_network_guid, std::move(success_callback),
+      std::move(error_callback));
 
   // In addition to disconnecting from the Wi-Fi network, this device must also
   // send a DisconnectTetheringRequest to the tether host so that it can shut
diff --git a/chromeos/components/tether/tether_disconnector_impl.h b/chromeos/components/tether/tether_disconnector_impl.h
index c8370d11d..6ef9ffde 100644
--- a/chromeos/components/tether/tether_disconnector_impl.h
+++ b/chromeos/components/tether/tether_disconnector_impl.h
@@ -14,7 +14,6 @@
 #include "base/memory/weak_ptr.h"
 #include "chromeos/components/tether/tether_disconnector.h"
 #include "chromeos/components/tether/tether_session_completion_logger.h"
-#include "chromeos/network/network_handler_callbacks.h"
 
 namespace chromeos {
 
@@ -40,18 +39,17 @@
   void DisconnectFromNetwork(
       const std::string& tether_network_guid,
       base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback,
+      StringErrorCallback error_callback,
       const TetherSessionCompletionLogger::SessionCompletionReason&
           session_completion_reason) override;
 
  private:
   friend class TetherDisconnectorImplTest;
 
-  void DisconnectActiveWifiConnection(
-      const std::string& tether_network_guid,
-      const std::string& wifi_network_guid,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback);
+  void DisconnectActiveWifiConnection(const std::string& tether_network_guid,
+                                      const std::string& wifi_network_guid,
+                                      base::OnceClosure success_callback,
+                                      StringErrorCallback error_callback);
 
   ActiveHost* active_host_;
   WifiHotspotDisconnector* wifi_hotspot_disconnector_;
diff --git a/chromeos/components/tether/wifi_hotspot_disconnector.h b/chromeos/components/tether/wifi_hotspot_disconnector.h
index 476d59b..ca904bc 100644
--- a/chromeos/components/tether/wifi_hotspot_disconnector.h
+++ b/chromeos/components/tether/wifi_hotspot_disconnector.h
@@ -7,7 +7,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "chromeos/network/network_handler_callbacks.h"
+#include "chromeos/network/network_connection_handler.h"
 
 namespace chromeos {
 
@@ -16,6 +16,9 @@
 // Disconnects from Wi-Fi hotspots provided by Tether hosts.
 class WifiHotspotDisconnector {
  public:
+  using StringErrorCallback =
+      NetworkConnectionHandler::TetherDelegate::StringErrorCallback;
+
   WifiHotspotDisconnector() {}
   virtual ~WifiHotspotDisconnector() {}
 
@@ -25,7 +28,7 @@
   virtual void DisconnectFromWifiHotspot(
       const std::string& wifi_network_guid,
       base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) = 0;
+      StringErrorCallback error_callback) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(WifiHotspotDisconnector);
diff --git a/chromeos/components/tether/wifi_hotspot_disconnector_impl.cc b/chromeos/components/tether/wifi_hotspot_disconnector_impl.cc
index ac9b30a0..2ffcbfc 100644
--- a/chromeos/components/tether/wifi_hotspot_disconnector_impl.cc
+++ b/chromeos/components/tether/wifi_hotspot_disconnector_impl.cc
@@ -5,6 +5,7 @@
 #include "chromeos/components/tether/wifi_hotspot_disconnector_impl.h"
 
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/tether/network_configuration_remover.h"
 #include "chromeos/components/tether/pref_names.h"
@@ -50,20 +51,20 @@
 void WifiHotspotDisconnectorImpl::DisconnectFromWifiHotspot(
     const std::string& wifi_network_guid,
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback) {
+    StringErrorCallback error_callback) {
   const NetworkState* wifi_network_state =
       network_state_handler_->GetNetworkStateFromGuid(wifi_network_guid);
   if (!wifi_network_state) {
     PA_LOG(ERROR) << "Wi-Fi NetworkState for GUID " << wifi_network_guid << " "
                   << "does not exist. Cannot disconnect.";
-    error_callback.Run(NetworkConnectionHandler::kErrorNotFound);
+    std::move(error_callback).Run(NetworkConnectionHandler::kErrorNotFound);
     return;
   }
 
   if (!wifi_network_state->IsConnectedState()) {
     PA_LOG(ERROR) << "Wi-Fi NetworkState for GUID " << wifi_network_guid << " "
                   << "is not connected. Cannot disconnect.";
-    error_callback.Run(NetworkConnectionHandler::kErrorNotConnected);
+    std::move(error_callback).Run(NetworkConnectionHandler::kErrorNotConnected);
     return;
   }
 
@@ -80,44 +81,46 @@
   pref_service_->Set(prefs::kDisconnectingWifiNetworkPath,
                      base::Value(wifi_network_path));
 
+  auto copyable_error_callback =
+      base::AdaptCallbackForRepeating(std::move(error_callback));
   network_connection_handler_->DisconnectNetwork(
       wifi_network_path,
       base::BindOnce(&WifiHotspotDisconnectorImpl::OnSuccessfulWifiDisconnect,
                      weak_ptr_factory_.GetWeakPtr(), wifi_network_guid,
                      wifi_network_path, std::move(success_callback),
-                     error_callback),
-      base::BindRepeating(&WifiHotspotDisconnectorImpl::OnFailedWifiDisconnect,
-                          weak_ptr_factory_.GetWeakPtr(), wifi_network_guid,
-                          wifi_network_path, error_callback));
+                     copyable_error_callback),
+      base::BindOnce(&WifiHotspotDisconnectorImpl::OnFailedWifiDisconnect,
+                     weak_ptr_factory_.GetWeakPtr(), wifi_network_guid,
+                     wifi_network_path, copyable_error_callback));
 }
 
 void WifiHotspotDisconnectorImpl::OnSuccessfulWifiDisconnect(
     const std::string& wifi_network_guid,
     const std::string& wifi_network_path,
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback) {
+    StringErrorCallback error_callback) {
   PA_LOG(VERBOSE) << "Successfully disconnected from Wi-Fi network with GUID "
                   << wifi_network_guid << ".";
   CleanUpAfterWifiDisconnection(wifi_network_path, std::move(success_callback),
-                                error_callback);
+                                std::move(error_callback));
 }
 
 void WifiHotspotDisconnectorImpl::OnFailedWifiDisconnect(
     const std::string& wifi_network_guid,
     const std::string& wifi_network_path,
-    const network_handler::StringResultCallback& error_callback,
+    StringErrorCallback error_callback,
     const std::string& error_name,
     std::unique_ptr<base::DictionaryValue> error_data) {
   PA_LOG(ERROR) << "Failed to disconnect from Wi-Fi network with GUID "
                 << wifi_network_guid << ". Error name: " << error_name;
   CleanUpAfterWifiDisconnection(wifi_network_path, base::OnceClosure(),
-                                error_callback);
+                                std::move(error_callback));
 }
 
 void WifiHotspotDisconnectorImpl::CleanUpAfterWifiDisconnection(
     const std::string& wifi_network_path,
     base::OnceClosure success_callback,
-    const network_handler::StringResultCallback& error_callback) {
+    StringErrorCallback error_callback) {
   network_configuration_remover_->RemoveNetworkConfigurationByPath(
       wifi_network_path);
   pref_service_->ClearPref(prefs::kDisconnectingWifiNetworkPath);
@@ -125,7 +128,8 @@
   if (success_callback)
     std::move(success_callback).Run();
   else
-    error_callback.Run(NetworkConnectionHandler::kErrorDisconnectFailed);
+    std::move(error_callback)
+        .Run(NetworkConnectionHandler::kErrorDisconnectFailed);
 }
 
 }  // namespace tether
diff --git a/chromeos/components/tether/wifi_hotspot_disconnector_impl.h b/chromeos/components/tether/wifi_hotspot_disconnector_impl.h
index e2c0b55..6ba92d1 100644
--- a/chromeos/components/tether/wifi_hotspot_disconnector_impl.h
+++ b/chromeos/components/tether/wifi_hotspot_disconnector_impl.h
@@ -35,27 +35,24 @@
   ~WifiHotspotDisconnectorImpl() override;
 
   // WifiHotspotDisconnector:
-  void DisconnectFromWifiHotspot(
-      const std::string& wifi_network_guid,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) override;
+  void DisconnectFromWifiHotspot(const std::string& wifi_network_guid,
+                                 base::OnceClosure success_callback,
+                                 StringErrorCallback error_callback) override;
 
  private:
-  void OnSuccessfulWifiDisconnect(
-      const std::string& wifi_network_guid,
-      const std::string& wifi_network_path,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback);
+  void OnSuccessfulWifiDisconnect(const std::string& wifi_network_guid,
+                                  const std::string& wifi_network_path,
+                                  base::OnceClosure success_callback,
+                                  StringErrorCallback error_callback);
   void OnFailedWifiDisconnect(
       const std::string& wifi_network_guid,
       const std::string& wifi_network_path,
-      const network_handler::StringResultCallback& error_callback,
+      StringErrorCallback error_callback,
       const std::string& error_name,
       std::unique_ptr<base::DictionaryValue> error_data);
-  void CleanUpAfterWifiDisconnection(
-      const std::string& wifi_network_path,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback);
+  void CleanUpAfterWifiDisconnection(const std::string& wifi_network_path,
+                                     base::OnceClosure success_callback,
+                                     StringErrorCallback error_callback);
 
   NetworkConnectionHandler* network_connection_handler_;
   NetworkStateHandler* network_state_handler_;
diff --git a/chromeos/components/web_applications/test/js_library_test.cc b/chromeos/components/web_applications/test/js_library_test.cc
index 0c31b06..fc6626d 100644
--- a/chromeos/components/web_applications/test/js_library_test.cc
+++ b/chromeos/components/web_applications/test/js_library_test.cc
@@ -17,24 +17,30 @@
 #include "content/public/browser/web_ui_controller_factory.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/common/url_constants.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "ui/webui/mojo_web_ui_controller.h"
 #include "url/gurl.h"
 
 namespace {
 
+constexpr base::FilePath::CharType kRootDir[] =
+    FILE_PATH_LITERAL("chromeos/components/system_apps/public/js/");
+
 constexpr char kSystemAppTestHost[] = "system-app-test";
+constexpr char kSystemAppTestURL[] = "chrome://system-app-test";
+constexpr char kUntrustedSystemAppTestURL[] =
+    "chrome-untrusted://system-app-test/";
 
 bool IsSystemAppTestURL(const GURL& url) {
   return url.SchemeIs(content::kChromeUIScheme) &&
          url.host() == kSystemAppTestHost;
 }
 
-void HandleRequest(const base::FilePath& root_dir,
-                   const std::string& url_path,
+void HandleRequest(const std::string& url_path,
                    content::WebUIDataSource::GotDataCallback callback) {
   base::FilePath path;
   CHECK(base::PathService::Get(base::BasePathKey::DIR_SOURCE_ROOT, &path));
-  path = path.Append(root_dir);
+  path = path.Append(kRootDir);
   path = path.AppendASCII(url_path.substr(0, url_path.find("?")));
 
   std::string contents;
@@ -43,37 +49,64 @@
     CHECK(base::ReadFileToString(path, &contents)) << path.value();
   }
 
-  scoped_refptr<base::RefCountedString> ref_contents(
-      new base::RefCountedString);
-  ref_contents->data() = contents;
-  std::move(callback).Run(ref_contents);
+  std::move(callback).Run(base::RefCountedString::TakeString(&contents));
+}
+
+void SetRequestFilterForDataSource(content::WebUIDataSource& data_source) {
+  data_source.SetRequestFilter(
+      base::BindRepeating([](const std::string& path) { return true; }),
+      base::BindRepeating(&HandleRequest));
+}
+
+content::WebUIDataSource* CreateTrustedSysemAppTestDataSource() {
+  auto* trusted_source = content::WebUIDataSource::Create(kSystemAppTestHost);
+
+  // We need a CSP override to be able to embed a chrome-untrusted:// iframe.
+  // TODO(crbug.com/1105408): use FrameSrc instead of ChildSrc.
+  std::string csp =
+      std::string("child-src ") + kUntrustedSystemAppTestURL + ";";
+  trusted_source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::ChildSrc, csp);
+
+  SetRequestFilterForDataSource(*trusted_source);
+  return trusted_source;
+}
+
+content::WebUIDataSource* CreateUntrustedSystemAppTestDataSource() {
+  auto* untrusted_source =
+      content::WebUIDataSource::Create(kUntrustedSystemAppTestURL);
+  untrusted_source->AddFrameAncestor(GURL(kSystemAppTestURL));
+
+  SetRequestFilterForDataSource(*untrusted_source);
+  return untrusted_source;
 }
 
 class JsLibraryTestWebUIController : public ui::MojoWebUIController {
  public:
-  explicit JsLibraryTestWebUIController(const base::FilePath& root_dir,
-                                        content::WebUI* web_ui)
+  explicit JsLibraryTestWebUIController(content::WebUI* web_ui)
       : ui::MojoWebUIController(web_ui) {
-    auto* data_source = content::WebUIDataSource::Create(kSystemAppTestHost);
-    data_source->SetRequestFilter(
-        base::BindRepeating([](const std::string& path) { return true; }),
-        base::BindRepeating(&HandleRequest, root_dir));
+    auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
 
-    content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
-                                  data_source);
+    content::WebUIDataSource::Add(browser_context,
+                                  CreateTrustedSysemAppTestDataSource());
+    content::WebUIDataSource::Add(browser_context,
+                                  CreateUntrustedSystemAppTestDataSource());
+
+    // Add ability to request chrome-untrusted: URLs
+    web_ui->AddRequestableScheme(content::kChromeUIUntrustedScheme);
   }
 };
 
 class JsLibraryTestWebUIControllerFactory
     : public content::WebUIControllerFactory {
  public:
-  explicit JsLibraryTestWebUIControllerFactory(const base::FilePath& root_dir)
-      : root_dir_(root_dir) {}
+  JsLibraryTestWebUIControllerFactory() = default;
+  ~JsLibraryTestWebUIControllerFactory() override = default;
 
   std::unique_ptr<content::WebUIController> CreateWebUIControllerForURL(
       content::WebUI* web_ui,
       const GURL& url) override {
-    return std::make_unique<JsLibraryTestWebUIController>(root_dir_, web_ui);
+    return std::make_unique<JsLibraryTestWebUIController>(web_ui);
   }
 
   content::WebUI::TypeID GetWebUIType(content::BrowserContext* browser_context,
@@ -93,16 +126,12 @@
                               const GURL& url) override {
     return IsSystemAppTestURL(url);
   }
-
- private:
-  const base::FilePath root_dir_;
 };
 
 }  // namespace
 
-JsLibraryTest::JsLibraryTest(const base::FilePath& root_dir)
-    : factory_(
-          std::make_unique<JsLibraryTestWebUIControllerFactory>(root_dir)) {
+JsLibraryTest::JsLibraryTest()
+    : factory_(std::make_unique<JsLibraryTestWebUIControllerFactory>()) {
   content::WebUIControllerFactory::RegisterFactory(factory_.get());
 }
 
diff --git a/chromeos/components/web_applications/test/js_library_test.h b/chromeos/components/web_applications/test/js_library_test.h
index af0033c..8213cdb 100644
--- a/chromeos/components/web_applications/test/js_library_test.h
+++ b/chromeos/components/web_applications/test/js_library_test.h
@@ -9,19 +9,16 @@
 
 #include "chrome/test/base/mojo_web_ui_browser_test.h"
 
-namespace base {
-class FilePath;
-}  // namespace base
-
 namespace content {
 class WebUIControllerFactory;
 }  // namespace content
 
-// Base test class used to test JS libraries for System Apps. It runs tests from
-// chrome://system-app-test and loads files from |root_dir|.
+// Base test class used to test JS libraries for System Apps. It setups
+// chrome://system-app-test and chrome-untrusted://system-app-test URLs and
+// loads files from chromeos/components/system_apps/public/js/.
 class JsLibraryTest : public MojoWebUIBrowserTest {
  public:
-  explicit JsLibraryTest(const base::FilePath& root_dir);
+  JsLibraryTest();
   ~JsLibraryTest() override;
 
   JsLibraryTest(const JsLibraryTest&) = delete;
diff --git a/chromeos/network/managed_network_configuration_handler.h b/chromeos/network/managed_network_configuration_handler.h
index db8075bd..cab18e0 100644
--- a/chromeos/network/managed_network_configuration_handler.h
+++ b/chromeos/network/managed_network_configuration_handler.h
@@ -99,7 +99,7 @@
   virtual void CreateConfiguration(
       const std::string& userhash,
       const base::DictionaryValue& properties,
-      const network_handler::ServiceResultCallback& callback,
+      network_handler::ServiceResultCallback callback,
       network_handler::ErrorCallback error_callback) const = 0;
 
   // Removes the user's configuration from the network with |service_path|. The
diff --git a/chromeos/network/managed_network_configuration_handler_impl.cc b/chromeos/network/managed_network_configuration_handler_impl.cc
index 0f6ba73..b75b3408 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.cc
+++ b/chromeos/network/managed_network_configuration_handler_impl.cc
@@ -339,7 +339,7 @@
 void ManagedNetworkConfigurationHandlerImpl::CreateConfiguration(
     const std::string& userhash,
     const base::DictionaryValue& properties,
-    const network_handler::ServiceResultCallback& callback,
+    network_handler::ServiceResultCallback callback,
     network_handler::ErrorCallback error_callback) const {
   std::string guid =
       GetStringFromDictionary(properties, ::onc::network_config::kGUID);
@@ -449,7 +449,7 @@
                                             validated_properties.get()));
 
   network_configuration_handler_->CreateShillConfiguration(
-      *shill_dictionary, callback, std::move(error_callback));
+      *shill_dictionary, std::move(callback), std::move(error_callback));
 }
 
 void ManagedNetworkConfigurationHandlerImpl::RemoveConfiguration(
diff --git a/chromeos/network/managed_network_configuration_handler_impl.h b/chromeos/network/managed_network_configuration_handler_impl.h
index d8a27fe..597e778e 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.h
+++ b/chromeos/network/managed_network_configuration_handler_impl.h
@@ -59,7 +59,7 @@
   void CreateConfiguration(
       const std::string& userhash,
       const base::DictionaryValue& properties,
-      const network_handler::ServiceResultCallback& callback,
+      network_handler::ServiceResultCallback callback,
       network_handler::ErrorCallback error_callback) const override;
 
   void RemoveConfiguration(
diff --git a/chromeos/network/mock_managed_network_configuration_handler.h b/chromeos/network/mock_managed_network_configuration_handler.h
index ef2794b..6fd0d527 100644
--- a/chromeos/network/mock_managed_network_configuration_handler.h
+++ b/chromeos/network/mock_managed_network_configuration_handler.h
@@ -37,12 +37,11 @@
                     const base::DictionaryValue& user_settings,
                     const base::Closure& callback,
                     network_handler::ErrorCallback error_callback));
-  MOCK_CONST_METHOD4(
-      CreateConfiguration,
-      void(const std::string& userhash,
-           const base::DictionaryValue& properties,
-           const network_handler::ServiceResultCallback& callback,
-           network_handler::ErrorCallback error_callback));
+  MOCK_CONST_METHOD4(CreateConfiguration,
+                     void(const std::string& userhash,
+                          const base::DictionaryValue& properties,
+                          network_handler::ServiceResultCallback callback,
+                          network_handler::ErrorCallback error_callback));
   MOCK_CONST_METHOD3(RemoveConfiguration,
                      void(const std::string& service_path,
                           const base::Closure& callback,
diff --git a/chromeos/network/network_configuration_handler.cc b/chromeos/network/network_configuration_handler.cc
index 636d7b9c..0e8fda4 100644
--- a/chromeos/network/network_configuration_handler.cc
+++ b/chromeos/network/network_configuration_handler.cc
@@ -338,7 +338,7 @@
 
 void NetworkConfigurationHandler::CreateShillConfiguration(
     const base::DictionaryValue& shill_properties,
-    const network_handler::ServiceResultCallback& callback,
+    network_handler::ServiceResultCallback callback,
     network_handler::ErrorCallback error_callback) {
   ShillManagerClient* manager = ShillManagerClient::Get();
   std::string type;
@@ -372,7 +372,7 @@
       dbus::ObjectPath(profile_path), *properties_to_set,
       base::BindOnce(&NetworkConfigurationHandler::ConfigurationCompleted,
                      weak_ptr_factory_.GetWeakPtr(), profile_path, guid,
-                     base::Passed(&properties_copy), callback),
+                     base::Passed(&properties_copy), std::move(callback)),
       base::BindOnce(&NetworkConfigurationHandler::ConfigurationFailed,
                      weak_ptr_factory_.GetWeakPtr(),
                      std::move(error_callback)));
@@ -474,7 +474,7 @@
       continue;
     }
     network_handler::ServiceResultCallback& callback = iter->second;
-    callback.Run(service_path, state->guid());
+    std::move(callback).Run(service_path, state->guid());
     iter = configure_callbacks_.erase(iter);
   }
 }
@@ -522,7 +522,7 @@
     const std::string& profile_path,
     const std::string& guid,
     std::unique_ptr<base::DictionaryValue> configure_properties,
-    const network_handler::ServiceResultCallback& callback,
+    network_handler::ServiceResultCallback callback,
     const dbus::ObjectPath& service_path) {
   // It is possible that the newly-configured network was already being tracked
   // by |network_state_handler_|. If this is the case, clear any existing error
@@ -542,7 +542,8 @@
   // |configure_callbacks_| will get triggered when NetworkStateHandler
   // notifies this that a state list update has occurred. |service_path|
   // is unique per configuration.
-  configure_callbacks_.insert(std::make_pair(service_path.value(), callback));
+  configure_callbacks_.insert(
+      std::make_pair(service_path.value(), std::move(callback)));
 }
 
 void NetworkConfigurationHandler::ProfileEntryDeleterCompleted(
diff --git a/chromeos/network/network_configuration_handler.h b/chromeos/network/network_configuration_handler.h
index bde22cbe..060bd85 100644
--- a/chromeos/network/network_configuration_handler.h
+++ b/chromeos/network/network_configuration_handler.h
@@ -94,10 +94,9 @@
   // Manager.ConfigureServiceForProfile. NOTE: Normally
   // ManagedNetworkConfigurationHandler should be used to call
   // CreateConfiguration. This will set GUID if not provided.
-  void CreateShillConfiguration(
-      const base::DictionaryValue& shill_properties,
-      const network_handler::ServiceResultCallback& callback,
-      network_handler::ErrorCallback error_callback);
+  void CreateShillConfiguration(const base::DictionaryValue& shill_properties,
+                                network_handler::ServiceResultCallback callback,
+                                network_handler::ErrorCallback error_callback);
 
   // Removes the network |service_path| from any profiles that include it.
   void RemoveConfiguration(const std::string& service_path,
@@ -147,7 +146,7 @@
       const std::string& profile_path,
       const std::string& guid,
       std::unique_ptr<base::DictionaryValue> configure_properties,
-      const network_handler::ServiceResultCallback& callback,
+      network_handler::ServiceResultCallback callback,
       const dbus::ObjectPath& service_path);
 
   void ConfigurationFailed(network_handler::ErrorCallback error_callback,
diff --git a/chromeos/network/network_connect_unittest.cc b/chromeos/network/network_connect_unittest.cc
index 04b5d989..5168b2a 100644
--- a/chromeos/network/network_connect_unittest.cc
+++ b/chromeos/network/network_connect_unittest.cc
@@ -67,17 +67,15 @@
   }
 
   // NetworkConnectionHandler::TetherDelegate:
-  void ConnectToNetwork(
-      const std::string& tether_network_guid,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) override {
+  void ConnectToNetwork(const std::string& tether_network_guid,
+                        base::OnceClosure success_callback,
+                        StringErrorCallback error_callback) override {
     last_connected_tether_network_guid_ = tether_network_guid;
     std::move(success_callback).Run();
   }
-  void DisconnectFromNetwork(
-      const std::string& tether_network_guid,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) override {}
+  void DisconnectFromNetwork(const std::string& tether_network_guid,
+                             base::OnceClosure success_callback,
+                             StringErrorCallback error_callback) override {}
 
  private:
   std::string last_connected_tether_network_guid_;
diff --git a/chromeos/network/network_connection_handler.cc b/chromeos/network/network_connection_handler.cc
index 582a6701..490f84c 100644
--- a/chromeos/network/network_connection_handler.cc
+++ b/chromeos/network/network_connection_handler.cc
@@ -116,11 +116,9 @@
       base::BindOnce(&NetworkConnectionHandler::InvokeConnectSuccessCallback,
                      weak_ptr_factory_.GetWeakPtr(), tether_network_guid,
                      std::move(success_callback)),
-      // TODO(crbug.com/1007660): Convert TetherDelegate to OnceCallback.
-      base::AdaptCallbackForRepeating(
-          base::BindOnce(&NetworkConnectionHandler::InvokeConnectErrorCallback,
-                         weak_ptr_factory_.GetWeakPtr(), tether_network_guid,
-                         std::move(error_callback))));
+      base::BindOnce(&NetworkConnectionHandler::InvokeConnectErrorCallback,
+                     weak_ptr_factory_.GetWeakPtr(), tether_network_guid,
+                     std::move(error_callback)));
 }
 
 void NetworkConnectionHandler::InitiateTetherNetworkDisconnection(
@@ -133,11 +131,9 @@
       base::BindOnce(&NetworkConnectionHandler::InvokeConnectSuccessCallback,
                      weak_ptr_factory_.GetWeakPtr(), tether_network_guid,
                      std::move(success_callback)),
-      // TODO(crbug.com/1007660): Convert TetherDelegate to OnceCallback.
-      base::AdaptCallbackForRepeating(
-          base::BindOnce(&NetworkConnectionHandler::InvokeConnectErrorCallback,
-                         weak_ptr_factory_.GetWeakPtr(), tether_network_guid,
-                         std::move(error_callback))));
+      base::BindOnce(&NetworkConnectionHandler::InvokeConnectErrorCallback,
+                     weak_ptr_factory_.GetWeakPtr(), tether_network_guid,
+                     std::move(error_callback)));
 }
 
 // static
diff --git a/chromeos/network/network_connection_handler.h b/chromeos/network/network_connection_handler.h
index 55cb965..90256cdd 100644
--- a/chromeos/network/network_connection_handler.h
+++ b/chromeos/network/network_connection_handler.h
@@ -110,21 +110,22 @@
 
   class COMPONENT_EXPORT(CHROMEOS_NETWORK) TetherDelegate {
    public:
+    using StringErrorCallback =
+        base::OnceCallback<void(const std::string& string_result)>;
+
     // Connects to the Tether network with GUID |tether_network_guid|. On
     // success, invokes |success_callback|, and on failure, invokes
     // |error_callback|, passing the relevant error code declared above.
-    virtual void ConnectToNetwork(
-        const std::string& tether_network_guid,
-        base::OnceClosure success_callback,
-        const network_handler::StringResultCallback& error_callback) = 0;
+    virtual void ConnectToNetwork(const std::string& tether_network_guid,
+                                  base::OnceClosure success_callback,
+                                  StringErrorCallback error_callback) = 0;
 
     // Disconnects from the Tether network with GUID |tether_network_guid|. On
     // success, invokes |success_callback|, and on failure, invokes
     // |error_callback|, passing the relevant error code declared above.
-    virtual void DisconnectFromNetwork(
-        const std::string& tether_network_guid,
-        base::OnceClosure success_callback,
-        const network_handler::StringResultCallback& error_callback) = 0;
+    virtual void DisconnectFromNetwork(const std::string& tether_network_guid,
+                                       base::OnceClosure success_callback,
+                                       StringErrorCallback error_callback) = 0;
 
    protected:
     virtual ~TetherDelegate() {}
diff --git a/chromeos/network/network_connection_handler_impl_unittest.cc b/chromeos/network/network_connection_handler_impl_unittest.cc
index 10f7b67..2c61a16 100644
--- a/chromeos/network/network_connection_handler_impl_unittest.cc
+++ b/chromeos/network/network_connection_handler_impl_unittest.cc
@@ -98,38 +98,34 @@
     return last_delegate_function_type_;
   }
 
-  void ConnectToNetwork(
-      const std::string& service_path,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) override {
+  void ConnectToNetwork(const std::string& service_path,
+                        base::OnceClosure success_callback,
+                        StringErrorCallback error_callback) override {
     last_delegate_function_type_ = DelegateFunctionType::CONNECT;
     last_service_path_ = service_path;
     last_success_callback_ = std::move(success_callback);
-    last_error_callback_ = error_callback;
+    last_error_callback_ = std::move(error_callback);
   }
 
-  void DisconnectFromNetwork(
-      const std::string& service_path,
-      base::OnceClosure success_callback,
-      const network_handler::StringResultCallback& error_callback) override {
+  void DisconnectFromNetwork(const std::string& service_path,
+                             base::OnceClosure success_callback,
+                             StringErrorCallback error_callback) override {
     last_delegate_function_type_ = DelegateFunctionType::DISCONNECT;
     last_service_path_ = service_path;
     last_success_callback_ = std::move(success_callback);
-    last_error_callback_ = error_callback;
+    last_error_callback_ = std::move(error_callback);
   }
 
   std::string& last_service_path() { return last_service_path_; }
 
   base::OnceClosure& last_success_callback() { return last_success_callback_; }
 
-  network_handler::StringResultCallback& last_error_callback() {
-    return last_error_callback_;
-  }
+  StringErrorCallback& last_error_callback() { return last_error_callback_; }
 
  private:
   std::string last_service_path_;
   base::OnceClosure last_success_callback_;
-  network_handler::StringResultCallback last_error_callback_;
+  StringErrorCallback last_error_callback_;
   DelegateFunctionType last_delegate_function_type_;
 };
 
@@ -587,8 +583,8 @@
   EXPECT_EQ(FakeTetherDelegate::DelegateFunctionType::CONNECT,
             fake_tether_delegate()->last_delegate_function_type());
   EXPECT_EQ(kTetherGuid, fake_tether_delegate()->last_service_path());
-  fake_tether_delegate()->last_error_callback().Run(
-      NetworkConnectionHandler::kErrorConnectFailed);
+  std::move(fake_tether_delegate()->last_error_callback())
+      .Run(NetworkConnectionHandler::kErrorConnectFailed);
   EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed, GetResultAndReset());
   EXPECT_TRUE(network_connection_observer()->GetRequested(kTetherGuid));
   EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed,
@@ -683,8 +679,8 @@
   EXPECT_EQ(FakeTetherDelegate::DelegateFunctionType::DISCONNECT,
             fake_tether_delegate()->last_delegate_function_type());
   EXPECT_EQ(kTetherGuid, fake_tether_delegate()->last_service_path());
-  fake_tether_delegate()->last_error_callback().Run(
-      NetworkConnectionHandler::kErrorConnectFailed);
+  std::move(fake_tether_delegate()->last_error_callback())
+      .Run(NetworkConnectionHandler::kErrorConnectFailed);
   EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed, GetResultAndReset());
   EXPECT_TRUE(network_connection_observer()->GetRequested(kTetherGuid));
   EXPECT_EQ(NetworkConnectionHandler::kErrorConnectFailed,
diff --git a/chromeos/network/network_device_handler_unittest.cc b/chromeos/network/network_device_handler_unittest.cc
index bebc704..0b6d750 100644
--- a/chromeos/network/network_device_handler_unittest.cc
+++ b/chromeos/network/network_device_handler_unittest.cc
@@ -79,11 +79,6 @@
                       base::Unretained(this));
   }
 
-  network_handler::StringResultCallback GetStringSuccessCallback() {
-    return base::Bind(&NetworkDeviceHandlerTest::StringSuccessCallback,
-                      base::Unretained(this));
-  }
-
   network_handler::ErrorCallback GetErrorCallback() {
     return base::BindOnce(&NetworkDeviceHandlerTest::ErrorCallback,
                           base::Unretained(this));
diff --git a/chromeos/network/network_handler_callbacks.h b/chromeos/network/network_handler_callbacks.h
index b414eda8d..85d02c1 100644
--- a/chromeos/network/network_handler_callbacks.h
+++ b/chromeos/network/network_handler_callbacks.h
@@ -42,12 +42,9 @@
     base::OnceCallback<void(const std::string& error_name,
                             std::unique_ptr<base::DictionaryValue> error_data)>;
 
-typedef base::Callback<void(const std::string& string_result)>
-    StringResultCallback;
-
-typedef base::Callback<void(const std::string& service_path,
-                            const std::string& guid)>
-    ServiceResultCallback;
+using ServiceResultCallback =
+    base::OnceCallback<void(const std::string& service_path,
+                            const std::string& guid)>;
 
 // Create a DictionaryValue for passing to ErrorCallback.
 COMPONENT_EXPORT(CHROMEOS_NETWORK)
diff --git a/components/autofill/content/browser/webauthn/internal_authenticator_impl.cc b/components/autofill/content/browser/webauthn/internal_authenticator_impl.cc
index e1291b98..43c1706 100644
--- a/components/autofill/content/browser/webauthn/internal_authenticator_impl.cc
+++ b/components/autofill/content/browser/webauthn/internal_authenticator_impl.cc
@@ -7,7 +7,6 @@
 #include <string>
 #include <utility>
 
-#include "base/timer/timer.h"
 #include "content/browser/webauth/authenticator_common.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
@@ -18,20 +17,12 @@
 
 InternalAuthenticatorImpl::InternalAuthenticatorImpl(
     RenderFrameHost* render_frame_host)
-    : InternalAuthenticatorImpl(render_frame_host,
-                                std::make_unique<AuthenticatorCommon>(
-                                    render_frame_host,
-                                    std::make_unique<base::OneShotTimer>())) {}
-
-InternalAuthenticatorImpl::InternalAuthenticatorImpl(
-    RenderFrameHost* render_frame_host,
-    std::unique_ptr<AuthenticatorCommon> authenticator_common)
     : WebContentsObserver(WebContents::FromRenderFrameHost(render_frame_host)),
       render_frame_host_(render_frame_host),
       effective_origin_(render_frame_host->GetLastCommittedOrigin()),
-      authenticator_common_(std::move(authenticator_common)) {
+      authenticator_common_(
+          std::make_unique<AuthenticatorCommon>(render_frame_host)) {
   DCHECK(render_frame_host_);
-  DCHECK(authenticator_common_);
   // Disabling WebAuthn modal dialogs to avoid conflict with Autofill's own
   // modal dialogs. Since WebAuthn is designed for websites, rather than browser
   // components, the UI can be confusing for users in the case for Autofill.
diff --git a/components/autofill/content/browser/webauthn/internal_authenticator_impl.h b/components/autofill/content/browser/webauthn/internal_authenticator_impl.h
index e80da274..064a554 100644
--- a/components/autofill/content/browser/webauthn/internal_authenticator_impl.h
+++ b/components/autofill/content/browser/webauthn/internal_authenticator_impl.h
@@ -57,14 +57,6 @@
  private:
   friend class InternalAuthenticatorImplTest;
 
-  // By being able to set AuthenticatorCommon, this constructor permits setting
-  // the connector and timer for testing. Using this constructor will also empty
-  // out the protocol set, since no device discovery will take place during
-  // tests.
-  InternalAuthenticatorImpl(
-      RenderFrameHost* render_frame_host,
-      std::unique_ptr<AuthenticatorCommon> authenticator_common);
-
   AuthenticatorCommon* get_authenticator_common_for_testing() {
     return authenticator_common_.get();
   }
diff --git a/components/exo/pointer.cc b/components/exo/pointer.cc
index cc5735f..494e1b2 100644
--- a/components/exo/pointer.cc
+++ b/components/exo/pointer.cc
@@ -24,6 +24,7 @@
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
+#include "ui/base/cursor/cursor_factory.h"
 #include "ui/base/cursor/cursor_size.h"
 #include "ui/base/cursor/cursor_util.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
@@ -41,10 +42,6 @@
 #include "chromeos/constants/chromeos_features.h"
 #endif
 
-#if defined(USE_OZONE)
-#include "ui/base/cursor/cursor_factory.h"
-#endif
-
 namespace exo {
 
 // Controls Pointer capture in exo/wayland.
@@ -685,24 +682,15 @@
                                               &bitmap, &hotspot);
 
     ui::PlatformCursor platform_cursor;
-#if defined(USE_OZONE)
     // TODO(reveman): Add interface for creating cursors from GpuMemoryBuffers
     // and use that here instead of the current bitmap API.
     // https://crbug.com/686600
     platform_cursor =
         ui::CursorFactory::GetInstance()->CreateImageCursor(bitmap, hotspot);
-#elif defined(USE_X11)
-    XcursorImage* image = ui::SkBitmapToXcursorImage(&bitmap, hotspot);
-    platform_cursor = ui::CreateReffedCustomXCursor(image);
-#endif
     cursor_.SetPlatformCursor(platform_cursor);
     cursor_.set_custom_bitmap(bitmap);
     cursor_.set_custom_hotspot(hotspot);
-#if defined(USE_OZONE)
     ui::CursorFactory::GetInstance()->UnrefImageCursor(platform_cursor);
-#elif defined(USE_X11)
-    ui::UnrefCustomXCursor(platform_cursor);
-#endif
   }
 
   // If there is a focused surface, update its widget as the views framework
diff --git a/components/favicon_base/favicon_types.h b/components/favicon_base/favicon_types.h
index 460254c..5809b6b3 100644
--- a/components/favicon_base/favicon_types.h
+++ b/components/favicon_base/favicon_types.h
@@ -29,7 +29,7 @@
 // blink::mojom::FaviconIconType enums
 //
 // The values of the IconTypes are used to select the priority in which favicon
-// data is returned in HistoryBackend and ThumbnailDatabase.
+// data is returned in HistoryBackend and FaviconDatabase.
 //
 // A Java counterpart will be generated for this enum.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.ui.favicon
diff --git a/components/feed/OWNERS b/components/feed/OWNERS
index 0cce6ad..85ef789 100644
--- a/components/feed/OWNERS
+++ b/components/feed/OWNERS
@@ -1,6 +1,14 @@
 carlosk@chromium.org
-fgorski@chromium.org
+chili@chromium.org
+dewittj@chromium.org
+dimich@chromium.org
 harringtond@chromium.org
+iwells@chromium.org
+jianli@chromium.org
+petewil@chromium.org
+
+# Jardin
+fgorski@chromium.org
 skym@chromium.org
 
 # Team: feed@chromium.org
diff --git a/components/history/core/browser/BUILD.gn b/components/history/core/browser/BUILD.gn
index 5a430f7..b6b41ad 100644
--- a/components/history/core/browser/BUILD.gn
+++ b/components/history/core/browser/BUILD.gn
@@ -20,6 +20,8 @@
     "download_types.h",
     "expire_history_backend.cc",
     "expire_history_backend.h",
+    "favicon_database.cc",
+    "favicon_database.h",
     "history_backend.cc",
     "history_backend.h",
     "history_backend_client.h",
@@ -58,8 +60,6 @@
     "sync/typed_url_sync_bridge.h",
     "sync/typed_url_sync_metadata_database.cc",
     "sync/typed_url_sync_metadata_database.h",
-    "thumbnail_database.cc",
-    "thumbnail_database.h",
     "top_sites.cc",
     "top_sites.h",
     "top_sites_backend.cc",
@@ -193,6 +193,7 @@
     "domain_mixing_metrics_unittest.cc",
     "download_slice_info_unittest.cc",
     "expire_history_backend_unittest.cc",
+    "favicon_database_unittest.cc",
     "history_backend_db_unittest.cc",
     "history_backend_unittest.cc",
     "history_database_unittest.cc",
@@ -202,7 +203,6 @@
     "sync/delete_directive_handler_unittest.cc",
     "sync/typed_url_sync_bridge_unittest.cc",
     "sync/typed_url_sync_metadata_database_unittest.cc",
-    "thumbnail_database_unittest.cc",
     "top_sites_database_unittest.cc",
     "top_sites_impl_unittest.cc",
     "url_database_unittest.cc",
diff --git a/components/history/core/browser/android/favicon_sql_handler.cc b/components/history/core/browser/android/favicon_sql_handler.cc
index 5f2c06f..2e6dad3 100644
--- a/components/history/core/browser/android/favicon_sql_handler.cc
+++ b/components/history/core/browser/android/favicon_sql_handler.cc
@@ -8,7 +8,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/stl_util.h"
-#include "components/history/core/browser/thumbnail_database.h"
+#include "components/history/core/browser/favicon_database.h"
 
 using base::Time;
 
@@ -22,9 +22,9 @@
 
 }  // namespace
 
-FaviconSQLHandler::FaviconSQLHandler(ThumbnailDatabase* thumbnail_db)
+FaviconSQLHandler::FaviconSQLHandler(FaviconDatabase* favicon_db)
     : SQLHandler(kInterestingColumns, base::size(kInterestingColumns)),
-      thumbnail_db_(thumbnail_db) {}
+      favicon_db_(favicon_db) {}
 
 FaviconSQLHandler::~FaviconSQLHandler() {
 }
@@ -37,7 +37,7 @@
   // If the image_data will be updated, it is not reasonable to find if the
   // icon is already in database, just create a new favicon.
   // TODO(pkotwicz): Pass in real pixel size.
-  favicon_base::FaviconID favicon_id = thumbnail_db_->AddFavicon(
+  favicon_base::FaviconID favicon_id = favicon_db_->AddFavicon(
       GURL(), favicon_base::IconType::kFavicon, row.favicon(),
       FaviconBitmapType::ON_VISIT, Time::Now(), gfx::Size());
 
@@ -49,18 +49,18 @@
        i != ids_set.end(); ++i) {
     // Remove all icon mappings to favicons of type kFavicon.
     std::vector<IconMapping> icon_mappings;
-    thumbnail_db_->GetIconMappingsForPageURL(
+    favicon_db_->GetIconMappingsForPageURL(
         i->url, {favicon_base::IconType::kFavicon}, &icon_mappings);
     for (std::vector<IconMapping>::const_iterator m = icon_mappings.begin();
          m != icon_mappings.end(); ++m) {
-      if (!thumbnail_db_->DeleteIconMapping(m->mapping_id))
+      if (!favicon_db_->DeleteIconMapping(m->mapping_id))
         return false;
 
       // Keep the old icon for deleting it later if possible.
       favicon_ids.push_back(m->icon_id);
     }
     // Add the icon mapping.
-    if (!thumbnail_db_->AddIconMapping(i->url, favicon_id))
+    if (!favicon_db_->AddIconMapping(i->url, favicon_id))
       return false;
   }
   // As we update the favicon, Let's remove unused favicons if any.
@@ -76,12 +76,12 @@
        i != ids_set.end(); ++i) {
     // Since the URL was deleted, we delete all types of icon mappings.
     std::vector<IconMapping> icon_mappings;
-    thumbnail_db_->GetIconMappingsForPageURL(i->url, &icon_mappings);
+    favicon_db_->GetIconMappingsForPageURL(i->url, &icon_mappings);
     for (std::vector<IconMapping>::const_iterator m = icon_mappings.begin();
          m != icon_mappings.end(); ++m) {
       favicon_ids.push_back(m->icon_id);
     }
-    if (!thumbnail_db_->DeleteIconMappings(i->url))
+    if (!favicon_db_->DeleteIconMappings(i->url))
       return false;
   }
 
@@ -103,12 +103,12 @@
 
   // Is it a problem to give a empty URL?
   // TODO(pkotwicz): Pass in real pixel size.
-  favicon_base::FaviconID id = thumbnail_db_->AddFavicon(
+  favicon_base::FaviconID id = favicon_db_->AddFavicon(
       GURL(), favicon_base::IconType::kFavicon, row->favicon(),
       FaviconBitmapType::ON_VISIT, Time::Now(), gfx::Size());
   if (!id)
     return false;
-  return thumbnail_db_->AddIconMapping(row->url(), id);
+  return favicon_db_->AddIconMapping(row->url(), id);
 }
 
 bool FaviconSQLHandler::DeleteUnusedFavicon(
@@ -116,7 +116,7 @@
   for (std::vector<favicon_base::FaviconID>::const_iterator i = ids.begin();
        i != ids.end();
        ++i) {
-    if (!thumbnail_db_->HasMappingFor(*i) && !thumbnail_db_->DeleteFavicon(*i))
+    if (!favicon_db_->HasMappingFor(*i) && !favicon_db_->DeleteFavicon(*i))
       return false;
   }
   return true;
diff --git a/components/history/core/browser/android/favicon_sql_handler.h b/components/history/core/browser/android/favicon_sql_handler.h
index df4e4fd..d8febbc 100644
--- a/components/history/core/browser/android/favicon_sql_handler.h
+++ b/components/history/core/browser/android/favicon_sql_handler.h
@@ -10,12 +10,12 @@
 
 namespace history {
 
-class ThumbnailDatabase;
+class FaviconDatabase;
 
 // The SQL handler implementation for icon_mapping and favicon table.
 class FaviconSQLHandler : public SQLHandler {
  public:
-  explicit FaviconSQLHandler(ThumbnailDatabase* thumbnail_db);
+  explicit FaviconSQLHandler(FaviconDatabase* favicon_db);
   ~FaviconSQLHandler() override;
 
   // SQLHandler overrides:
@@ -29,7 +29,7 @@
   // true if all unused favicons are deleted.
   bool DeleteUnusedFavicon(const std::vector<favicon_base::FaviconID>& ids);
 
-  ThumbnailDatabase* thumbnail_db_;
+  FaviconDatabase* favicon_db_;
 
   DISALLOW_COPY_AND_ASSIGN(FaviconSQLHandler);
 };
diff --git a/components/history/core/browser/expire_history_backend.cc b/components/history/core/browser/expire_history_backend.cc
index 7fee452..74fda30 100644
--- a/components/history/core/browser/expire_history_backend.cc
+++ b/components/history/core/browser/expire_history_backend.cc
@@ -21,10 +21,10 @@
 #include "base/sequenced_task_runner.h"
 #include "base/stl_util.h"
 #include "build/build_config.h"
+#include "components/history/core/browser/favicon_database.h"
 #include "components/history/core/browser/history_backend_client.h"
 #include "components/history/core/browser/history_backend_notifier.h"
 #include "components/history/core/browser/history_database.h"
-#include "components/history/core/browser/thumbnail_database.h"
 
 namespace history {
 
@@ -168,7 +168,7 @@
     scoped_refptr<base::SequencedTaskRunner> task_runner)
     : notifier_(notifier),
       main_db_(nullptr),
-      thumb_db_(nullptr),
+      favicon_db_(nullptr),
       backend_client_(backend_client),
       task_runner_(task_runner) {
   DCHECK(notifier_);
@@ -178,9 +178,9 @@
 }
 
 void ExpireHistoryBackend::SetDatabases(HistoryDatabase* main_db,
-                                        ThumbnailDatabase* thumb_db) {
+                                        FaviconDatabase* favicon_db) {
   main_db_ = main_db;
-  thumb_db_ = thumb_db;
+  favicon_db_ = favicon_db;
 }
 
 void ExpireHistoryBackend::DeleteURL(const GURL& url, base::Time end_time) {
@@ -334,7 +334,7 @@
 
 void ExpireHistoryBackend::ClearOldOnDemandFaviconsIfPossible(
     base::Time expiration_threshold) {
-  if (!thumb_db_)
+  if (!favicon_db_)
     return;
 
   // Extra precaution to avoid repeated calls to GetOldOnDemandFavicons() close
@@ -348,7 +348,7 @@
   last_on_demand_expiration_threshold_ = expiration_threshold;
 
   std::map<favicon_base::FaviconID, IconMappingsForExpiry> icon_mappings =
-      thumb_db_->GetOldOnDemandFavicons(expiration_threshold);
+      favicon_db_->GetOldOnDemandFavicons(expiration_threshold);
   DeleteEffects effects;
 
   for (auto id_and_mappings_pair : icon_mappings) {
@@ -360,8 +360,8 @@
       continue;
     }
 
-    thumb_db_->DeleteFavicon(icon_id);
-    thumb_db_->DeleteIconMappingsForFaviconId(icon_id);
+    favicon_db_->DeleteFavicon(icon_id);
+    favicon_db_->DeleteIconMappingsForFaviconId(icon_id);
     effects.deleted_favicons.insert(mappings.icon_url);
   }
 
@@ -408,18 +408,16 @@
 }
 
 void ExpireHistoryBackend::DeleteFaviconsIfPossible(DeleteEffects* effects) {
-  if (!thumb_db_)
+  if (!favicon_db_)
     return;
 
   for (auto i = effects->affected_favicons.begin();
        i != effects->affected_favicons.end(); ++i) {
-    if (!thumb_db_->HasMappingFor(*i)) {
+    if (!favicon_db_->HasMappingFor(*i)) {
       GURL icon_url;
       favicon_base::IconType icon_type;
-      if (thumb_db_->GetFaviconHeader(*i,
-                                      &icon_url,
-                                      &icon_type) &&
-          thumb_db_->DeleteFavicon(*i)) {
+      if (favicon_db_->GetFaviconHeader(*i, &icon_url, &icon_type) &&
+          favicon_db_->DeleteFavicon(*i)) {
         effects->deleted_favicons.insert(icon_url);
       }
     }
@@ -494,12 +492,13 @@
                                        DeleteEffects* effects) {
   // Collect shared information.
   std::vector<IconMapping> icon_mappings;
-  if (thumb_db_ && thumb_db_->GetIconMappingsForPageURL(gurl, &icon_mappings)) {
+  if (favicon_db_ &&
+      favicon_db_->GetIconMappingsForPageURL(gurl, &icon_mappings)) {
     for (auto m = icon_mappings.begin(); m != icon_mappings.end(); ++m) {
       effects->affected_favicons.insert(m->icon_id);
     }
     // Delete the mapping entries for the url.
-    thumb_db_->DeleteIconMappings(gurl);
+    favicon_db_->DeleteIconMappings(gurl);
   }
 }
 
diff --git a/components/history/core/browser/expire_history_backend.h b/components/history/core/browser/expire_history_backend.h
index 44a30e4..bfd0d89 100644
--- a/components/history/core/browser/expire_history_backend.h
+++ b/components/history/core/browser/expire_history_backend.h
@@ -26,10 +26,10 @@
 
 namespace history {
 
+class FaviconDatabase;
 class HistoryBackendClient;
 class HistoryBackendNotifier;
 class HistoryDatabase;
-class ThumbnailDatabase;
 
 // Encapsulates visit expiration criteria and type of visits to expire.
 class ExpiringVisitsReader {
@@ -65,8 +65,7 @@
   ~ExpireHistoryBackend();
 
   // Completes initialization by setting the databases that this class will use.
-  void SetDatabases(HistoryDatabase* main_db,
-                    ThumbnailDatabase* thumb_db);
+  void SetDatabases(HistoryDatabase* main_db, FaviconDatabase* favicon_db);
 
   // Begins periodic expiration of history older than the given threshold. This
   // will continue until the object is deleted.
@@ -98,9 +97,8 @@
   // accordingly.
   void ExpireHistoryBeforeForTesting(base::Time end_time);
 
-  // Clears all old on-demand favicons from thumbnail database. Fails silently
-  // (we don't care about favicons so much, so don't want to stop everything if
-  // it fails).
+  // Clears all old on-demand favicons. Fails silently (we don't care about
+  // favicons so much, so don't want to stop everything if it fails).
   void ClearOldOnDemandFaviconsIfPossible(base::Time expiration_threshold);
 
   // Returns the current cut-off time before which we will start expiring stuff.
@@ -149,7 +147,7 @@
     // will need to check when the delete operations are complete.
     std::set<favicon_base::FaviconID> affected_favicons;
 
-    // All favicon urls that were actually deleted from the thumbnail db.
+    // All favicon urls that were deleted from the favicon db.
     std::set<GURL> deleted_favicons;
   };
 
@@ -163,7 +161,7 @@
                               DeleteEffects* effects);
 
   // Finds or deletes dependency information for the given URL. Information that
-  // is specific to this URL (URL row, thumbnails, etc.) is deleted.
+  // is specific to this URL (URL row, favicons, etc.) is deleted.
   //
   // This does not affect the visits! This is used for expiration as well as
   // deleting from the UI, and they handle visits differently.
@@ -176,7 +174,7 @@
   //
   // Assumes the main_db_ is non-NULL.
   //
-  // NOTE: If the url is pinned, we keep the favicons and thumbnails.
+  // NOTE: If the url is pinned, we keep the favicons.
   void DeleteOneURL(const URLRow& url_row,
                     bool is_pinned,
                     DeleteEffects* effects);
@@ -269,7 +267,7 @@
 
   // Non-owning pointers to the databases we deal with (MAY BE NULL).
   HistoryDatabase* main_db_;       // Main history database.
-  ThumbnailDatabase* thumb_db_;    // Thumbnails and favicons.
+  FaviconDatabase* favicon_db_;
 
   // The threshold for "old" history where we will automatically delete it.
   base::TimeDelta expiration_threshold_;
diff --git a/components/history/core/browser/expire_history_backend_unittest.cc b/components/history/core/browser/expire_history_backend_unittest.cc
index 41190cd..945bd824 100644
--- a/components/history/core/browser/expire_history_backend_unittest.cc
+++ b/components/history/core/browser/expire_history_backend_unittest.cc
@@ -24,11 +24,11 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "components/history/core/browser/favicon_database.h"
 #include "components/history/core/browser/history_backend_client.h"
 #include "components/history/core/browser/history_backend_notifier.h"
 #include "components/history/core/browser/history_constants.h"
 #include "components/history/core/browser/history_database.h"
-#include "components/history/core/browser/thumbnail_database.h"
 #include "components/history/core/browser/top_sites.h"
 #include "components/history/core/browser/top_sites_impl.h"
 #include "components/history/core/browser/top_sites_observer.h"
@@ -140,7 +140,7 @@
 
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
   std::unique_ptr<HistoryDatabase> main_db_;
-  std::unique_ptr<ThumbnailDatabase> thumb_db_;
+  std::unique_ptr<FaviconDatabase> thumb_db_;
   scoped_refptr<TopSitesImpl> top_sites_;
 
   // base::Time at the beginning of the test, so everybody agrees what "now" is.
@@ -162,7 +162,7 @@
       main_db_.reset();
 
     base::FilePath thumb_name = path().Append(kFaviconsFilename);
-    thumb_db_ = std::make_unique<ThumbnailDatabase>();
+    thumb_db_ = std::make_unique<FaviconDatabase>();
     if (thumb_db_->Init(thumb_name) != sql::INIT_OK)
       thumb_db_.reset();
 
@@ -952,10 +952,7 @@
   EXPECT_EQ(0, new_url_row2.typed_count());
   EXPECT_EQ(0, new_url_row2.visit_count());
 
-  // Thumbnails and favicons should still exist. Note that we keep thumbnails
-  // that may have been updated since the time threshold. Since the URL still
-  // exists in history, this should not be a privacy problem, we only update
-  // the visit counts in this case for consistency anyway.
+  // Favicons should still exist.
   favicon_base::FaviconID favicon_id =
       GetFavicon(url_row1.url(), favicon_base::IconType::kFavicon);
   EXPECT_TRUE(HasFavicon(favicon_id));
diff --git a/components/history/core/browser/thumbnail_database.cc b/components/history/core/browser/favicon_database.cc
similarity index 83%
rename from components/history/core/browser/thumbnail_database.cc
rename to components/history/core/browser/favicon_database.cc
index a47fdd3..aa28038 100644
--- a/components/history/core/browser/thumbnail_database.cc
+++ b/components/history/core/browser/favicon_database.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/history/core/browser/thumbnail_database.h"
+#include "components/history/core/browser/favicon_database.h"
 
 #include <stddef.h>
 #include <stdint.h>
@@ -107,27 +107,11 @@
   icon_mapping->mapping_id = statement.ColumnInt64(0);
   icon_mapping->icon_id = statement.ColumnInt64(1);
   icon_mapping->icon_type =
-      ThumbnailDatabase::FromPersistedIconType(statement.ColumnInt(2));
+      FaviconDatabase::FromPersistedIconType(statement.ColumnInt(2));
   icon_mapping->icon_url = GURL(statement.ColumnString(3));
   icon_mapping->page_url = page_url;
 }
 
-enum InvalidStructureType {
-  // NOTE(shess): Intentionally skip bucket 0 to account for
-  // conversion from a boolean histogram.
-  STRUCTURE_EVENT_FAVICON = 1,
-  STRUCTURE_EVENT_VERSION4,
-  STRUCTURE_EVENT_VERSION5,
-
-  // Always keep this at the end.
-  STRUCTURE_EVENT_MAX,
-};
-
-void RecordInvalidStructure(InvalidStructureType invalid_type) {
-  UMA_HISTOGRAM_ENUMERATION("History.InvalidFaviconsDBStructure",
-                            invalid_type, STRUCTURE_EVENT_MAX);
-}
-
 // NOTE(shess): Schema modifications must consider initial creation in
 // |InitImpl()| and history pruning in |RetainDataForPageUrls()|.
 bool InitTables(sql::Database* db) {
@@ -247,13 +231,11 @@
 
 }  // namespace
 
-ThumbnailDatabase::IconMappingEnumerator::IconMappingEnumerator() {
-}
+FaviconDatabase::IconMappingEnumerator::IconMappingEnumerator() {}
 
-ThumbnailDatabase::IconMappingEnumerator::~IconMappingEnumerator() {
-}
+FaviconDatabase::IconMappingEnumerator::~IconMappingEnumerator() {}
 
-bool ThumbnailDatabase::IconMappingEnumerator::GetNextIconMapping(
+bool FaviconDatabase::IconMappingEnumerator::GetNextIconMapping(
     IconMapping* icon_mapping) {
   if (!statement_.Step())
     return false;
@@ -261,13 +243,13 @@
   return true;
 }
 
-ThumbnailDatabase::ThumbnailDatabase() = default;
+FaviconDatabase::FaviconDatabase() = default;
 
-ThumbnailDatabase::~ThumbnailDatabase() {
+FaviconDatabase::~FaviconDatabase() {
   // The DBCloseScoper will delete the DB and the cache.
 }
 
-sql::InitStatus ThumbnailDatabase::Init(const base::FilePath& db_name) {
+sql::InitStatus FaviconDatabase::Init(const base::FilePath& db_name) {
   // TODO(shess): Consider separating database open from schema setup.
   // With that change, this code could Raze() from outside the
   // transaction, rather than needing RazeAndClose() in InitImpl().
@@ -287,7 +269,7 @@
   return status;
 }
 
-void ThumbnailDatabase::ComputeDatabaseMetrics() {
+void FaviconDatabase::ComputeDatabaseMetrics() {
   base::TimeTicks start_time = base::TimeTicks::Now();
 
   // Calculate the size of the favicon database.
@@ -299,8 +281,8 @@
     sql::Statement page_size(
         db_.GetCachedStatement(SQL_FROM_HERE, "PRAGMA page_size"));
     int64_t page_size_bytes = page_size.Step() ? page_size.ColumnInt64(0) : 0;
-    int size_mb = static_cast<int>(
-        (page_count_bytes * page_size_bytes) / (1024 * 1024));
+    int size_mb =
+        static_cast<int>((page_count_bytes * page_size_bytes) / (1024 * 1024));
     UMA_HISTOGRAM_MEMORY_MB("History.FaviconDatabaseSizeMB", size_mb);
   }
 
@@ -315,9 +297,8 @@
 
   // Count all bitmap resources cached in the DB.
   {
-    sql::Statement bitmap_count(
-        db_.GetCachedStatement(
-            SQL_FROM_HERE, "SELECT COUNT(*) FROM favicon_bitmaps"));
+    sql::Statement bitmap_count(db_.GetCachedStatement(
+        SQL_FROM_HERE, "SELECT COUNT(*) FROM favicon_bitmaps"));
     UMA_HISTOGRAM_COUNTS_10000(
         "History.NumFaviconBitmapsInDB",
         bitmap_count.Step() ? bitmap_count.ColumnInt(0) : 0);
@@ -325,10 +306,9 @@
 
   // Count "touch" icon URLs referenced by the DB.
   {
-    sql::Statement touch_icon_count(
-        db_.GetCachedStatement(
-            SQL_FROM_HERE,
-            "SELECT COUNT(*) FROM favicons WHERE icon_type IN (?, ?)"));
+    sql::Statement touch_icon_count(db_.GetCachedStatement(
+        SQL_FROM_HERE,
+        "SELECT COUNT(*) FROM favicons WHERE icon_type IN (?, ?)"));
     touch_icon_count.BindInt64(
         0, ToPersistedIconType(favicon_base::IconType::kTouchIcon));
     touch_icon_count.BindInt64(
@@ -340,10 +320,9 @@
 
   // Count "large" bitmap resources cached in the DB.
   {
-    sql::Statement large_bitmap_count(
-        db_.GetCachedStatement(
-            SQL_FROM_HERE,
-            "SELECT COUNT(*) FROM favicon_bitmaps WHERE width >= 64"));
+    sql::Statement large_bitmap_count(db_.GetCachedStatement(
+        SQL_FROM_HERE,
+        "SELECT COUNT(*) FROM favicon_bitmaps WHERE width >= 64"));
     UMA_HISTOGRAM_COUNTS_10000(
         "History.NumLargeFaviconBitmapsInDB",
         large_bitmap_count.Step() ? large_bitmap_count.ColumnInt(0) : 0);
@@ -351,43 +330,42 @@
 
   // Count all icon mappings maintained by the DB.
   {
-    sql::Statement mapping_count(
-        db_.GetCachedStatement(
-            SQL_FROM_HERE, "SELECT COUNT(*) FROM icon_mapping"));
+    sql::Statement mapping_count(db_.GetCachedStatement(
+        SQL_FROM_HERE, "SELECT COUNT(*) FROM icon_mapping"));
     UMA_HISTOGRAM_CUSTOM_COUNTS(
         "History.NumFaviconMappingsInDB",
-        (mapping_count.Step() ? mapping_count.ColumnInt(0) : 0),
-        1, 100000, 100);
+        (mapping_count.Step() ? mapping_count.ColumnInt(0) : 0), 1, 100000,
+        100);
   }
 
   UMA_HISTOGRAM_TIMES("History.FaviconDatabaseAdvancedMetricsTime",
                       base::TimeTicks::Now() - start_time);
 }
 
-void ThumbnailDatabase::BeginTransaction() {
+void FaviconDatabase::BeginTransaction() {
   db_.BeginTransaction();
 }
 
-void ThumbnailDatabase::CommitTransaction() {
+void FaviconDatabase::CommitTransaction() {
   db_.CommitTransaction();
 }
 
-void ThumbnailDatabase::RollbackTransaction() {
+void FaviconDatabase::RollbackTransaction() {
   db_.RollbackTransaction();
 }
 
-void ThumbnailDatabase::Vacuum() {
-  DCHECK(db_.transaction_nesting() == 0) <<
-      "Can not have a transaction when vacuuming.";
+void FaviconDatabase::Vacuum() {
+  DCHECK(db_.transaction_nesting() == 0)
+      << "Can not have a transaction when vacuuming.";
   ignore_result(db_.Execute("VACUUM"));
 }
 
-void ThumbnailDatabase::TrimMemory() {
+void FaviconDatabase::TrimMemory() {
   db_.TrimMemory();
 }
 
 std::map<favicon_base::FaviconID, IconMappingsForExpiry>
-ThumbnailDatabase::GetOldOnDemandFavicons(base::Time threshold) {
+FaviconDatabase::GetOldOnDemandFavicons(base::Time threshold) {
   // Restrict to on-demand bitmaps (i.e. with last_requested != 0).
   // This is called rarely during history expiration cleanup and hence not worth
   // caching.
@@ -411,7 +389,7 @@
   return icon_mappings;
 }
 
-bool ThumbnailDatabase::GetFaviconBitmapIDSizes(
+bool FaviconDatabase::GetFaviconBitmapIDSizes(
     favicon_base::FaviconID icon_id,
     std::vector<FaviconBitmapIDSize>* bitmap_id_sizes) {
   DCHECK(icon_id);
@@ -428,18 +406,19 @@
 
     FaviconBitmapIDSize bitmap_id_size;
     bitmap_id_size.bitmap_id = statement.ColumnInt64(0);
-    bitmap_id_size.pixel_size = gfx::Size(statement.ColumnInt(1),
-                                          statement.ColumnInt(2));
+    bitmap_id_size.pixel_size =
+        gfx::Size(statement.ColumnInt(1), statement.ColumnInt(2));
     bitmap_id_sizes->push_back(bitmap_id_size);
   }
   return result;
 }
 
-bool ThumbnailDatabase::GetFaviconBitmaps(
+bool FaviconDatabase::GetFaviconBitmaps(
     favicon_base::FaviconID icon_id,
     std::vector<FaviconBitmap>* favicon_bitmaps) {
   DCHECK(icon_id);
-  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
+  sql::Statement statement(db_.GetCachedStatement(
+      SQL_FROM_HERE,
       "SELECT id, last_updated, image_data, width, height, last_requested "
       "FROM favicon_bitmaps WHERE icon_id=?"));
   statement.BindInt64(0, icon_id);
@@ -460,8 +439,8 @@
       statement.ColumnBlobAsVector(2, &data->data());
       favicon_bitmap.bitmap_data = data;
     }
-    favicon_bitmap.pixel_size = gfx::Size(statement.ColumnInt(3),
-                                          statement.ColumnInt(4));
+    favicon_bitmap.pixel_size =
+        gfx::Size(statement.ColumnInt(3), statement.ColumnInt(4));
     favicon_bitmap.last_requested = base::Time::FromDeltaSinceWindowsEpoch(
         base::TimeDelta::FromMicroseconds(statement.ColumnInt64(5)));
     favicon_bitmaps->push_back(favicon_bitmap);
@@ -469,14 +448,15 @@
   return result;
 }
 
-bool ThumbnailDatabase::GetFaviconBitmap(
+bool FaviconDatabase::GetFaviconBitmap(
     FaviconBitmapID bitmap_id,
     base::Time* last_updated,
     base::Time* last_requested,
     scoped_refptr<base::RefCountedMemory>* png_icon_data,
     gfx::Size* pixel_size) {
   DCHECK(bitmap_id);
-  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
+  sql::Statement statement(db_.GetCachedStatement(
+      SQL_FROM_HERE,
       "SELECT last_updated, image_data, width, height, last_requested "
       "FROM favicon_bitmaps WHERE id=?"));
   statement.BindInt64(0, bitmap_id);
@@ -496,8 +476,7 @@
   }
 
   if (pixel_size) {
-    *pixel_size = gfx::Size(statement.ColumnInt(2),
-                            statement.ColumnInt(3));
+    *pixel_size = gfx::Size(statement.ColumnInt(2), statement.ColumnInt(3));
   }
 
   if (last_requested) {
@@ -508,7 +487,7 @@
   return true;
 }
 
-FaviconBitmapID ThumbnailDatabase::AddFaviconBitmap(
+FaviconBitmapID FaviconDatabase::AddFaviconBitmap(
     favicon_base::FaviconID icon_id,
     const scoped_refptr<base::RefCountedMemory>& icon_data,
     FaviconBitmapType type,
@@ -551,7 +530,7 @@
   return db_.GetLastInsertRowId();
 }
 
-bool ThumbnailDatabase::SetFaviconBitmap(
+bool FaviconDatabase::SetFaviconBitmap(
     FaviconBitmapID bitmap_id,
     scoped_refptr<base::RefCountedMemory> bitmap_data,
     base::Time time) {
@@ -576,9 +555,8 @@
   return statement.Run();
 }
 
-bool ThumbnailDatabase::SetFaviconBitmapLastUpdateTime(
-    FaviconBitmapID bitmap_id,
-    base::Time time) {
+bool FaviconDatabase::SetFaviconBitmapLastUpdateTime(FaviconBitmapID bitmap_id,
+                                                     base::Time time) {
   DCHECK(bitmap_id);
   // By updating last_updated timestamp, we assume the icon is of type ON_VISIT.
   // If it is ON_DEMAND, reset last_requested to 0 and thus silently change the
@@ -593,8 +571,8 @@
   return statement.Run();
 }
 
-bool ThumbnailDatabase::TouchOnDemandFavicon(const GURL& icon_url,
-                                             base::Time time) {
+bool FaviconDatabase::TouchOnDemandFavicon(const GURL& icon_url,
+                                           base::Time time) {
   // Look up the icon ids for the url.
   sql::Statement id_statement(db_.GetCachedStatement(
       SQL_FROM_HERE, "SELECT id FROM favicons WHERE url=?"));
@@ -624,15 +602,16 @@
   return true;
 }
 
-bool ThumbnailDatabase::DeleteFaviconBitmap(FaviconBitmapID bitmap_id) {
-  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
-      "DELETE FROM favicon_bitmaps WHERE id=?"));
+bool FaviconDatabase::DeleteFaviconBitmap(FaviconBitmapID bitmap_id) {
+  sql::Statement statement(db_.GetCachedStatement(
+      SQL_FROM_HERE, "DELETE FROM favicon_bitmaps WHERE id=?"));
   statement.BindInt64(0, bitmap_id);
   return statement.Run();
 }
 
-bool ThumbnailDatabase::SetFaviconOutOfDate(favicon_base::FaviconID icon_id) {
-  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
+bool FaviconDatabase::SetFaviconOutOfDate(favicon_base::FaviconID icon_id) {
+  sql::Statement statement(db_.GetCachedStatement(
+      SQL_FROM_HERE,
       "UPDATE favicon_bitmaps SET last_updated=? WHERE icon_id=?"));
   statement.BindInt64(0, 0);
   statement.BindInt64(1, icon_id);
@@ -640,9 +619,8 @@
   return statement.Run();
 }
 
-bool ThumbnailDatabase::GetFaviconLastUpdatedTime(
-    favicon_base::FaviconID icon_id,
-    base::Time* last_updated) {
+bool FaviconDatabase::GetFaviconLastUpdatedTime(favicon_base::FaviconID icon_id,
+                                                base::Time* last_updated) {
   sql::Statement statement(db_.GetCachedStatement(
       SQL_FROM_HERE,
       "SELECT MAX(last_updated) FROM favicon_bitmaps WHERE icon_id=?"));
@@ -662,7 +640,7 @@
   return true;
 }
 
-favicon_base::FaviconID ThumbnailDatabase::GetFaviconIDForFaviconURL(
+favicon_base::FaviconID FaviconDatabase::GetFaviconIDForFaviconURL(
     const GURL& icon_url,
     favicon_base::IconType icon_type) {
   sql::Statement statement(db_.GetCachedStatement(
@@ -676,13 +654,13 @@
   return statement.ColumnInt64(0);
 }
 
-bool ThumbnailDatabase::GetFaviconHeader(favicon_base::FaviconID icon_id,
-                                         GURL* icon_url,
-                                         favicon_base::IconType* icon_type) {
+bool FaviconDatabase::GetFaviconHeader(favicon_base::FaviconID icon_id,
+                                       GURL* icon_url,
+                                       favicon_base::IconType* icon_type) {
   DCHECK(icon_id);
 
-  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
-      "SELECT url, icon_type FROM favicons WHERE id=?"));
+  sql::Statement statement(db_.GetCachedStatement(
+      SQL_FROM_HERE, "SELECT url, icon_type FROM favicons WHERE id=?"));
   statement.BindInt64(0, icon_id);
 
   if (!statement.Step())
@@ -696,12 +674,11 @@
   return true;
 }
 
-favicon_base::FaviconID ThumbnailDatabase::AddFavicon(
+favicon_base::FaviconID FaviconDatabase::AddFavicon(
     const GURL& icon_url,
     favicon_base::IconType icon_type) {
-
-  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
-      "INSERT INTO favicons (url, icon_type) VALUES (?, ?)"));
+  sql::Statement statement(db_.GetCachedStatement(
+      SQL_FROM_HERE, "INSERT INTO favicons (url, icon_type) VALUES (?, ?)"));
   statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url));
   statement.BindInt(1, ToPersistedIconType(icon_type));
 
@@ -710,7 +687,7 @@
   return db_.GetLastInsertRowId();
 }
 
-favicon_base::FaviconID ThumbnailDatabase::AddFavicon(
+favicon_base::FaviconID FaviconDatabase::AddFavicon(
     const GURL& icon_url,
     favicon_base::IconType icon_type,
     const scoped_refptr<base::RefCountedMemory>& icon_data,
@@ -724,21 +701,21 @@
   return icon_id;
 }
 
-bool ThumbnailDatabase::DeleteFavicon(favicon_base::FaviconID id) {
+bool FaviconDatabase::DeleteFavicon(favicon_base::FaviconID id) {
   sql::Statement statement;
   statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
-      "DELETE FROM favicons WHERE id = ?"));
+                                          "DELETE FROM favicons WHERE id = ?"));
   statement.BindInt64(0, id);
   if (!statement.Run())
     return false;
 
-  statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
-      "DELETE FROM favicon_bitmaps WHERE icon_id = ?"));
+  statement.Assign(db_.GetCachedStatement(
+      SQL_FROM_HERE, "DELETE FROM favicon_bitmaps WHERE icon_id = ?"));
   statement.BindInt64(0, id);
   return statement.Run();
 }
 
-bool ThumbnailDatabase::GetIconMappingsForPageURL(
+bool FaviconDatabase::GetIconMappingsForPageURL(
     const GURL& page_url,
     const favicon_base::IconTypeSet& required_icon_types,
     std::vector<IconMapping>* filtered_mapping_data) {
@@ -759,10 +736,11 @@
   return result;
 }
 
-bool ThumbnailDatabase::GetIconMappingsForPageURL(
+bool FaviconDatabase::GetIconMappingsForPageURL(
     const GURL& page_url,
     std::vector<IconMapping>* mapping_data) {
-  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
+  sql::Statement statement(db_.GetCachedStatement(
+      SQL_FROM_HERE,
       "SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type, "
       "favicons.url "
       "FROM icon_mapping "
@@ -785,7 +763,7 @@
   return result;
 }
 
-base::Optional<GURL> ThumbnailDatabase::FindFirstPageURLForHost(
+base::Optional<GURL> FaviconDatabase::FindFirstPageURLForHost(
     const GURL& url,
     const favicon_base::IconTypeSet& required_icon_types) {
   if (url.host().empty())
@@ -806,7 +784,7 @@
 
   while (statement.Step()) {
     favicon_base::IconType icon_type =
-        ThumbnailDatabase::FromPersistedIconType(statement.ColumnInt(1));
+        FaviconDatabase::FromPersistedIconType(statement.ColumnInt(1));
 
     if (required_icon_types.count(icon_type) != 0)
       return base::make_optional(GURL(statement.ColumnString(0)));
@@ -814,9 +792,8 @@
   return base::nullopt;
 }
 
-IconMappingID ThumbnailDatabase::AddIconMapping(
-    const GURL& page_url,
-    favicon_base::FaviconID icon_id) {
+IconMappingID FaviconDatabase::AddIconMapping(const GURL& page_url,
+                                              favicon_base::FaviconID icon_id) {
   static const char kSql[] =
       "INSERT INTO icon_mapping (page_url, icon_id) VALUES (?, ?)";
   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSql));
@@ -829,15 +806,15 @@
   return db_.GetLastInsertRowId();
 }
 
-bool ThumbnailDatabase::DeleteIconMappings(const GURL& page_url) {
-  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
-      "DELETE FROM icon_mapping WHERE page_url = ?"));
+bool FaviconDatabase::DeleteIconMappings(const GURL& page_url) {
+  sql::Statement statement(db_.GetCachedStatement(
+      SQL_FROM_HERE, "DELETE FROM icon_mapping WHERE page_url = ?"));
   statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
 
   return statement.Run();
 }
 
-bool ThumbnailDatabase::DeleteIconMappingsForFaviconId(
+bool FaviconDatabase::DeleteIconMappingsForFaviconId(
     favicon_base::FaviconID id) {
   // This is called rarely during history expiration cleanup and hence not
   // worth caching.
@@ -847,39 +824,39 @@
   return statement.Run();
 }
 
-bool ThumbnailDatabase::DeleteIconMapping(IconMappingID mapping_id) {
-  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
-      "DELETE FROM icon_mapping WHERE id=?"));
+bool FaviconDatabase::DeleteIconMapping(IconMappingID mapping_id) {
+  sql::Statement statement(db_.GetCachedStatement(
+      SQL_FROM_HERE, "DELETE FROM icon_mapping WHERE id=?"));
   statement.BindInt64(0, mapping_id);
 
   return statement.Run();
 }
 
-bool ThumbnailDatabase::HasMappingFor(favicon_base::FaviconID id) {
+bool FaviconDatabase::HasMappingFor(favicon_base::FaviconID id) {
   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
-      "SELECT id FROM icon_mapping "
-      "WHERE icon_id=?"));
+                                                  "SELECT id FROM icon_mapping "
+                                                  "WHERE icon_id=?"));
   statement.BindInt64(0, id);
 
   return statement.Step();
 }
 
-bool ThumbnailDatabase::InitIconMappingEnumerator(
+bool FaviconDatabase::InitIconMappingEnumerator(
     favicon_base::IconType type,
     IconMappingEnumerator* enumerator) {
   DCHECK(!enumerator->statement_.is_valid());
   enumerator->statement_.Assign(db_.GetCachedStatement(
       SQL_FROM_HERE,
       "SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type, "
-             "favicons.url, icon_mapping.page_url "
-         "FROM icon_mapping JOIN favicons ON ("
-              "icon_mapping.icon_id = favicons.id) "
-         "WHERE favicons.icon_type = ?"));
+      "favicons.url, icon_mapping.page_url "
+      "FROM icon_mapping JOIN favicons ON ("
+      "icon_mapping.icon_id = favicons.id) "
+      "WHERE favicons.icon_type = ?"));
   enumerator->statement_.BindInt(0, ToPersistedIconType(type));
   return enumerator->statement_.is_valid();
 }
 
-bool ThumbnailDatabase::RetainDataForPageUrls(
+bool FaviconDatabase::RetainDataForPageUrls(
     const std::vector<GURL>& urls_to_keep) {
   sql::Transaction transaction(&db_);
   if (!transaction.Begin())
@@ -980,8 +957,7 @@
     return false;
 
   // Copy all of the data over.
-  if (!db_.Execute(kCopyIconMapping) ||
-      !db_.Execute(kCopyFavicons) ||
+  if (!db_.Execute(kCopyIconMapping) || !db_.Execute(kCopyFavicons) ||
       !db_.Execute(kCopyFaviconBitmaps)) {
     return false;
   }
@@ -1008,7 +984,7 @@
 }
 
 // static
-int ThumbnailDatabase::ToPersistedIconType(favicon_base::IconType icon_type) {
+int FaviconDatabase::ToPersistedIconType(favicon_base::IconType icon_type) {
   if (icon_type == favicon_base::IconType::kInvalid)
     return 0;
 
@@ -1016,7 +992,7 @@
 }
 
 // static
-favicon_base::IconType ThumbnailDatabase::FromPersistedIconType(int icon_type) {
+favicon_base::IconType FaviconDatabase::FromPersistedIconType(int icon_type) {
   if (icon_type == 0)
     return favicon_base::IconType::kInvalid;
 
@@ -1027,14 +1003,14 @@
   return static_cast<favicon_base::IconType>(val);
 }
 
-sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Database* db,
-                                                const base::FilePath& db_name) {
+sql::InitStatus FaviconDatabase::OpenDatabase(sql::Database* db,
+                                              const base::FilePath& db_name) {
   db->set_histogram_tag("Thumbnail");
   db->set_error_callback(
       base::BindRepeating(&DatabaseErrorCallback, db, db_name));
 
-  // Thumbnails db now only stores favicons, so we don't need that big a page
-  // size or cache.
+  // Favicons db only stores favicons, so we don't need that big a page size or
+  // cache.
   db->set_page_size(2048);
   db->set_cache_size(32);
 
@@ -1049,7 +1025,7 @@
   return sql::INIT_OK;
 }
 
-sql::InitStatus ThumbnailDatabase::InitImpl(const base::FilePath& db_name) {
+sql::InitStatus FaviconDatabase::InitImpl(const base::FilePath& db_name) {
   sql::InitStatus status = OpenDatabase(&db_, db_name);
   if (status != sql::INIT_OK)
     return status;
@@ -1068,11 +1044,11 @@
   if (!transaction.Begin())
     return sql::INIT_FAILURE;
 
-  // TODO(shess): Failing Begin() implies that something serious is
-  // wrong with the database.  Raze() may be in order.
+    // TODO(shess): Failing Begin() implies that something serious is
+    // wrong with the database.  Raze() may be in order.
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
-  // Exclude the thumbnails file from backups.
+  // Exclude the favicons file from backups.
   base::mac::SetFileBackupExclusion(db_name);
 #endif
 
@@ -1092,15 +1068,14 @@
   // Create the tables.
   if (!meta_table_.Init(&db_, kCurrentVersionNumber,
                         kCompatibleVersionNumber) ||
-      !InitTables(&db_) ||
-      !InitIndices(&db_)) {
+      !InitTables(&db_) || !InitIndices(&db_)) {
     return sql::INIT_FAILURE;
   }
 
   // Version check. We should not encounter a database too old for us to handle
   // in the wild, so we try to continue in that case.
   if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
-    LOG(WARNING) << "Thumbnail database is too new.";
+    LOG(WARNING) << "Favicon database is too new.";
     return sql::INIT_TOO_NEW;
   }
 
@@ -1108,7 +1083,6 @@
 
   if (!db_.DoesColumnExist("favicons", "icon_type")) {
     LOG(ERROR) << "Raze because of missing favicon.icon_type";
-    RecordInvalidStructure(STRUCTURE_EVENT_VERSION4);
 
     db_.RazeAndClose();
     return sql::INIT_FAILURE;
@@ -1116,7 +1090,6 @@
 
   if (cur_version < 7 && !db_.DoesColumnExist("favicons", "sizes")) {
     LOG(ERROR) << "Raze because of missing favicon.sizes";
-    RecordInvalidStructure(STRUCTURE_EVENT_VERSION5);
 
     db_.RazeAndClose();
     return sql::INIT_FAILURE;
@@ -1134,8 +1107,8 @@
       return CantUpgradeToVersion(cur_version);
   }
 
-  LOG_IF(WARNING, cur_version < kCurrentVersionNumber) <<
-      "Thumbnail database version " << cur_version << " is too old to handle.";
+  LOG_IF(WARNING, cur_version < kCurrentVersionNumber)
+      << "Favicon database version " << cur_version << " is too old to handle.";
 
   // Initialization is complete.
   if (!transaction.Commit())
@@ -1149,7 +1122,6 @@
   // (crbug.com/166453)
   if (IsFaviconDBStructureIncorrect()) {
     LOG(ERROR) << "Raze because of invalid favicon db structure.";
-    RecordInvalidStructure(STRUCTURE_EVENT_FAVICON);
 
     db_.RazeAndClose();
     return sql::INIT_FAILURE;
@@ -1158,14 +1130,14 @@
   return sql::INIT_OK;
 }
 
-sql::InitStatus ThumbnailDatabase::CantUpgradeToVersion(int cur_version) {
-  LOG(WARNING) << "Unable to update to thumbnail database to version " <<
-               cur_version << ".";
+sql::InitStatus FaviconDatabase::CantUpgradeToVersion(int cur_version) {
+  LOG(WARNING) << "Unable to update to favicon database to version "
+               << cur_version << ".";
   db_.Close();
   return sql::INIT_FAILURE;
 }
 
-bool ThumbnailDatabase::UpgradeToVersion7() {
+bool FaviconDatabase::UpgradeToVersion7() {
   // Sizes column was never used, remove it.
   bool success =
       db_.Execute(
@@ -1190,7 +1162,7 @@
   return true;
 }
 
-bool ThumbnailDatabase::UpgradeToVersion8() {
+bool FaviconDatabase::UpgradeToVersion8() {
   // Add the last_requested column to the favicon_bitmaps table.
   static const char kFaviconBitmapsAddLastRequestedSql[] =
       "ALTER TABLE favicon_bitmaps ADD COLUMN last_requested INTEGER DEFAULT 0";
@@ -1202,7 +1174,7 @@
   return true;
 }
 
-bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() {
+bool FaviconDatabase::IsFaviconDBStructureIncorrect() {
   return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons");
 }
 
diff --git a/components/history/core/browser/thumbnail_database.h b/components/history/core/browser/favicon_database.h
similarity index 89%
rename from components/history/core/browser/thumbnail_database.h
rename to components/history/core/browser/favicon_database.h
index a32a49f5..9ba8e6f 100644
--- a/components/history/core/browser/thumbnail_database.h
+++ b/components/history/core/browser/favicon_database.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_HISTORY_CORE_BROWSER_THUMBNAIL_DATABASE_H_
-#define COMPONENTS_HISTORY_CORE_BROWSER_THUMBNAIL_DATABASE_H_
+#ifndef COMPONENTS_HISTORY_CORE_BROWSER_FAVICON_DATABASE_H_
+#define COMPONENTS_HISTORY_CORE_BROWSER_FAVICON_DATABASE_H_
 
 #include <vector>
 
@@ -21,7 +21,7 @@
 class FilePath;
 class RefCountedMemory;
 class Time;
-}
+}  // namespace base
 
 namespace history {
 
@@ -30,16 +30,11 @@
 static const int kFaviconUpdateLastRequestedAfterDays = 10;
 
 // This database interface is owned by the history backend and runs on the
-// history thread. It is a totally separate component from history partially
-// because we may want to move it to its own thread in the future. The
-// operations we will do on this database will be slow, but we can tolerate
-// higher latency (it's OK for thumbnails to come in slower than the rest
-// of the data). Moving this to a separate thread would not block potentially
-// higher priority history operations.
-class ThumbnailDatabase {
+// history thread.
+class FaviconDatabase {
  public:
-  ThumbnailDatabase();
-  ~ThumbnailDatabase();
+  FaviconDatabase();
+  ~FaviconDatabase();
 
   // Must be called after creation but before any other methods are called.
   // When not INIT_OK, no other functions should be called.
@@ -52,9 +47,7 @@
   // Transactions on the database.
   void BeginTransaction();
   void CommitTransaction();
-  int transaction_nesting() const {
-    return db_.transaction_nesting();
-  }
+  int transaction_nesting() const { return db_.transaction_nesting(); }
   void RollbackTransaction();
 
   // Vacuums the database. This will cause sqlite to defragment and collect
@@ -241,7 +234,7 @@
     bool GetNextIconMapping(IconMapping* icon_mapping);
 
    private:
-    friend class ThumbnailDatabase;
+    friend class FaviconDatabase;
 
     // Used to query database and return the data for filling IconMapping in
     // each call of GetNextIconMapping().
@@ -266,16 +259,16 @@
   static favicon_base::IconType FromPersistedIconType(int icon_type);
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(ThumbnailDatabaseTest, RetainDataForPageUrls);
-  FRIEND_TEST_ALL_PREFIXES(ThumbnailDatabaseTest,
+  FRIEND_TEST_ALL_PREFIXES(FaviconDatabaseTest, RetainDataForPageUrls);
+  FRIEND_TEST_ALL_PREFIXES(FaviconDatabaseTest,
                            RetainDataForPageUrlsExpiresRetainedFavicons);
-  FRIEND_TEST_ALL_PREFIXES(ThumbnailDatabaseTest, Version3);
-  FRIEND_TEST_ALL_PREFIXES(ThumbnailDatabaseTest, Version4);
-  FRIEND_TEST_ALL_PREFIXES(ThumbnailDatabaseTest, Version5);
-  FRIEND_TEST_ALL_PREFIXES(ThumbnailDatabaseTest, Version6);
-  FRIEND_TEST_ALL_PREFIXES(ThumbnailDatabaseTest, Version7);
-  FRIEND_TEST_ALL_PREFIXES(ThumbnailDatabaseTest, Version8);
-  FRIEND_TEST_ALL_PREFIXES(ThumbnailDatabaseTest, WildSchema);
+  FRIEND_TEST_ALL_PREFIXES(FaviconDatabaseTest, Version3);
+  FRIEND_TEST_ALL_PREFIXES(FaviconDatabaseTest, Version4);
+  FRIEND_TEST_ALL_PREFIXES(FaviconDatabaseTest, Version5);
+  FRIEND_TEST_ALL_PREFIXES(FaviconDatabaseTest, Version6);
+  FRIEND_TEST_ALL_PREFIXES(FaviconDatabaseTest, Version7);
+  FRIEND_TEST_ALL_PREFIXES(FaviconDatabaseTest, Version8);
+  FRIEND_TEST_ALL_PREFIXES(FaviconDatabaseTest, WildSchema);
 
   // Open database on a given filename. If the file does not exist,
   // it is created.
@@ -310,4 +303,4 @@
 
 }  // namespace history
 
-#endif  // COMPONENTS_HISTORY_CORE_BROWSER_THUMBNAIL_DATABASE_H_
+#endif  // COMPONENTS_HISTORY_CORE_BROWSER_FAVICON_DATABASE_H_
diff --git a/components/history/core/browser/thumbnail_database_unittest.cc b/components/history/core/browser/favicon_database_unittest.cc
similarity index 90%
rename from components/history/core/browser/thumbnail_database_unittest.cc
rename to components/history/core/browser/favicon_database_unittest.cc
index a2f408e..ebe1c52 100644
--- a/components/history/core/browser/thumbnail_database_unittest.cc
+++ b/components/history/core/browser/favicon_database_unittest.cc
@@ -14,7 +14,7 @@
 #include "base/path_service.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
-#include "components/history/core/browser/thumbnail_database.h"
+#include "components/history/core/browser/favicon_database.h"
 #include "components/history/core/test/database_test_utils.h"
 #include "sql/database.h"
 #include "sql/recovery.h"
@@ -107,7 +107,7 @@
 
 // Adds a favicon at |icon_url| with |icon_type| with default bitmap data and
 // maps |page_url| to |icon_url|.
-void AddAndMapFaviconSimple(ThumbnailDatabase* db,
+void AddAndMapFaviconSimple(FaviconDatabase* db,
                             const GURL& page_url,
                             const GURL& icon_url,
                             favicon_base::IconType icon_type) {
@@ -131,7 +131,7 @@
 
 // Helper to check that an expected mapping exists.
 WARN_UNUSED_RESULT bool CheckPageHasIcon(
-    ThumbnailDatabase* db,
+    FaviconDatabase* db,
     const GURL& page_url,
     favicon_base::IconType expected_icon_type,
     const GURL& expected_icon_url,
@@ -182,8 +182,8 @@
     return false;
   }
 
-  if (memcmp(favicon_bitmaps[0].bitmap_data->front(),
-             expected_icon_contents, expected_icon_contents_size)) {
+  if (memcmp(favicon_bitmaps[0].bitmap_data->front(), expected_icon_contents,
+             expected_icon_contents_size)) {
     ADD_FAILURE() << "failed to match |expected_icon_contents|";
     return false;
   }
@@ -200,21 +200,20 @@
 
 }  // namespace
 
-class ThumbnailDatabaseTest : public testing::Test {
+class FaviconDatabaseTest : public testing::Test {
  public:
-  ThumbnailDatabaseTest() {}
-  ~ThumbnailDatabaseTest() override {}
+  FaviconDatabaseTest() {}
+  ~FaviconDatabaseTest() override {}
 
-  // Initialize a thumbnail database instance from the SQL file at
+  // Initialize a favicon database instance from the SQL file at
   // |golden_path| in the "History/" subdirectory of test data.
-  std::unique_ptr<ThumbnailDatabase> LoadFromGolden(const char* golden_path) {
+  std::unique_ptr<FaviconDatabase> LoadFromGolden(const char* golden_path) {
     if (!CreateDatabaseFromSQL(file_name_, golden_path)) {
       ADD_FAILURE() << "Failed loading " << golden_path;
-      return std::unique_ptr<ThumbnailDatabase>();
+      return std::unique_ptr<FaviconDatabase>();
     }
 
-    std::unique_ptr<ThumbnailDatabase> db =
-        std::make_unique<ThumbnailDatabase>();
+    std::unique_ptr<FaviconDatabase> db = std::make_unique<FaviconDatabase>();
     EXPECT_EQ(sql::INIT_OK, db->Init(file_name_));
     db->BeginTransaction();
 
@@ -233,8 +232,8 @@
   base::FilePath file_name_;
 };
 
-TEST_F(ThumbnailDatabaseTest, AddIconMapping) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, AddIconMapping) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -256,9 +255,8 @@
   EXPECT_EQ(id, icon_mappings.front().icon_id);
 }
 
-TEST_F(ThumbnailDatabaseTest,
-       AddOnDemandFaviconBitmapCreatesCorrectTimestamps) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, AddOnDemandFaviconBitmapCreatesCorrectTimestamps) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -284,8 +282,8 @@
   EXPECT_EQ(add_time, last_requested);
 }
 
-TEST_F(ThumbnailDatabaseTest, AddFaviconBitmapCreatesCorrectTimestamps) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, AddFaviconBitmapCreatesCorrectTimestamps) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -311,9 +309,8 @@
   EXPECT_EQ(base::Time(), last_requested);
 }
 
-TEST_F(ThumbnailDatabaseTest,
-       GetFaviconLastUpdatedTimeReturnsFalseForNoBitmaps) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, GetFaviconLastUpdatedTimeReturnsFalseForNoBitmaps) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -326,8 +323,8 @@
   ASSERT_FALSE(db.GetFaviconLastUpdatedTime(icon, &last_updated));
 }
 
-TEST_F(ThumbnailDatabaseTest, GetFaviconLastUpdatedTimeReturnsMaxTime) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, GetFaviconLastUpdatedTimeReturnsMaxTime) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -354,8 +351,8 @@
   EXPECT_EQ(add_time1, last_updated);
 }
 
-TEST_F(ThumbnailDatabaseTest, TouchUpdatesOnDemandFavicons) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, TouchUpdatesOnDemandFavicons) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -386,8 +383,8 @@
   EXPECT_EQ(end, last_requested);  // Updates the last_requested field.
 }
 
-TEST_F(ThumbnailDatabaseTest, TouchUpdatesOnlyInfrequently) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, TouchUpdatesOnlyInfrequently) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -414,8 +411,8 @@
   EXPECT_EQ(start, last_requested);  // No update.
 }
 
-TEST_F(ThumbnailDatabaseTest, TouchDoesNotUpdateStandardFavicons) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, TouchDoesNotUpdateStandardFavicons) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -445,10 +442,10 @@
   EXPECT_EQ(base::Time(), last_requested);  // No update.
 }
 
-// Test that ThumbnailDatabase::GetOldOnDemandFavicons() returns on-demand icons
+// Test that FaviconDatabase::GetOldOnDemandFavicons() returns on-demand icons
 // which were requested prior to the passed in timestamp.
-TEST_F(ThumbnailDatabaseTest, GetOldOnDemandFaviconsReturnsOld) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, GetOldOnDemandFaviconsReturnsOld) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -478,12 +475,12 @@
                                          ElementsAre(page_url1, page_url2))))));
 }
 
-// Test that ThumbnailDatabase::GetOldOnDemandFavicons() returns on-visit icons
+// Test that FaviconDatabase::GetOldOnDemandFavicons() returns on-visit icons
 // if the on-visit icons have expired. We need this behavior in order to delete
 // icons stored via HistoryService::SetOnDemandFavicons() prior to on-demand
 // icons setting the "last_requested" time.
-TEST_F(ThumbnailDatabaseTest, GetOldOnDemandFaviconsDoesNotReturnExpired) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, GetOldOnDemandFaviconsDoesNotReturnExpired) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -508,10 +505,10 @@
   EXPECT_TRUE(map.empty());
 }
 
-// Test that ThumbnailDatabase::GetOldOnDemandFavicons() does not return
+// Test that FaviconDatabase::GetOldOnDemandFavicons() does not return
 // on-demand icons which were requested after the passed in timestamp.
-TEST_F(ThumbnailDatabaseTest, GetOldOnDemandFaviconsDoesNotReturnFresh) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, GetOldOnDemandFaviconsDoesNotReturnFresh) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -538,10 +535,10 @@
   EXPECT_TRUE(map.empty());
 }
 
-// Test that ThumbnailDatabase::GetOldOnDemandFavicons() does not return
+// Test that FaviconDatabase::GetOldOnDemandFavicons() does not return
 // non-expired on-visit icons.
-TEST_F(ThumbnailDatabaseTest, GetOldOnDemandFaviconsDoesNotDeleteStandard) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, GetOldOnDemandFaviconsDoesNotDeleteStandard) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -563,8 +560,8 @@
   EXPECT_TRUE(map.empty());
 }
 
-TEST_F(ThumbnailDatabaseTest, DeleteIconMappings) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, DeleteIconMappings) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -598,8 +595,8 @@
       url, {favicon_base::IconType::kFavicon}, nullptr));
 }
 
-TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURL) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, GetIconMappingsForPageURL) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -631,8 +628,8 @@
   EXPECT_EQ(id2, icon_mappings[1].icon_id);
 }
 
-TEST_F(ThumbnailDatabaseTest, RetainDataForPageUrls) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, RetainDataForPageUrls) {
+  FaviconDatabase db;
 
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
 
@@ -705,8 +702,8 @@
 }
 
 // Test that RetainDataForPageUrls() expires retained favicons.
-TEST_F(ThumbnailDatabaseTest, RetainDataForPageUrlsExpiresRetainedFavicons) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, RetainDataForPageUrlsExpiresRetainedFavicons) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -731,8 +728,8 @@
 
 // Tests that deleting a favicon deletes the favicon row and favicon bitmap
 // rows from the database.
-TEST_F(ThumbnailDatabaseTest, DeleteFavicon) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, DeleteFavicon) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -758,8 +755,8 @@
   EXPECT_FALSE(db.GetFaviconBitmaps(id, nullptr));
 }
 
-TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLForReturnOrder) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, GetIconMappingsForPageURLForReturnOrder) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -823,8 +820,8 @@
 
 // Test that when multiple icon types are passed to GetIconMappingsForPageURL()
 // that the results are filtered according to the passed in types.
-TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLWithIconTypes) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, GetIconMappingsForPageURLWithIconTypes) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -852,8 +849,8 @@
   EXPECT_EQ(IconUrl2(), icon_mappings[2].icon_url);
 }
 
-TEST_F(ThumbnailDatabaseTest, FindFirstPageURLForHost) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, FindFirstPageURLForHost) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -904,8 +901,8 @@
   EXPECT_EQ(IconUrl5(), icon_mappings[0].icon_url);
 }
 
-TEST_F(ThumbnailDatabaseTest, HasMappingFor) {
-  ThumbnailDatabase db;
+TEST_F(FaviconDatabaseTest, HasMappingFor) {
+  FaviconDatabase db;
   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
   db.BeginTransaction();
 
@@ -950,8 +947,8 @@
 }
 
 // Test loading version 3 database.
-TEST_F(ThumbnailDatabaseTest, Version3) {
-  std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v3.sql");
+TEST_F(FaviconDatabaseTest, Version3) {
+  std::unique_ptr<FaviconDatabase> db = LoadFromGolden("Favicons.v3.sql");
   ASSERT_TRUE(db);
   VerifyTablesAndColumns(&db->db_);
 
@@ -960,8 +957,8 @@
 }
 
 // Test loading version 4 database.
-TEST_F(ThumbnailDatabaseTest, Version4) {
-  std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v4.sql");
+TEST_F(FaviconDatabaseTest, Version4) {
+  std::unique_ptr<FaviconDatabase> db = LoadFromGolden("Favicons.v4.sql");
   ASSERT_TRUE(db);
   VerifyTablesAndColumns(&db->db_);
 
@@ -970,8 +967,8 @@
 }
 
 // Test loading version 5 database.
-TEST_F(ThumbnailDatabaseTest, Version5) {
-  std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v5.sql");
+TEST_F(FaviconDatabaseTest, Version5) {
+  std::unique_ptr<FaviconDatabase> db = LoadFromGolden("Favicons.v5.sql");
   ASSERT_TRUE(db);
   VerifyTablesAndColumns(&db->db_);
 
@@ -980,8 +977,8 @@
 }
 
 // Test loading version 6 database.
-TEST_F(ThumbnailDatabaseTest, Version6) {
-  std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v6.sql");
+TEST_F(FaviconDatabaseTest, Version6) {
+  std::unique_ptr<FaviconDatabase> db = LoadFromGolden("Favicons.v6.sql");
   ASSERT_TRUE(db);
   VerifyTablesAndColumns(&db->db_);
 
@@ -990,8 +987,8 @@
 }
 
 // Test loading version 7 database.
-TEST_F(ThumbnailDatabaseTest, Version7) {
-  std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v7.sql");
+TEST_F(FaviconDatabaseTest, Version7) {
+  std::unique_ptr<FaviconDatabase> db = LoadFromGolden("Favicons.v7.sql");
   ASSERT_TRUE(db);
   VerifyTablesAndColumns(&db->db_);
 
@@ -1010,8 +1007,8 @@
 }
 
 // Test loading version 8 database.
-TEST_F(ThumbnailDatabaseTest, Version8) {
-  std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v8.sql");
+TEST_F(FaviconDatabaseTest, Version8) {
+  std::unique_ptr<FaviconDatabase> db = LoadFromGolden("Favicons.v8.sql");
   ASSERT_TRUE(db);
   VerifyTablesAndColumns(&db->db_);
 
@@ -1029,7 +1026,7 @@
                                kLargeSize, sizeof(kBlob2), kBlob2));
 }
 
-TEST_F(ThumbnailDatabaseTest, Recovery) {
+TEST_F(FaviconDatabaseTest, Recovery) {
   // Create an example database.
   {
     EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v8.sql"));
@@ -1041,7 +1038,7 @@
 
   // Test that the contents make sense after clean open.
   {
-    ThumbnailDatabase db;
+    FaviconDatabase db;
     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
 
     EXPECT_TRUE(CheckPageHasIcon(&db, PageUrl1(),
@@ -1076,7 +1073,7 @@
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);
-    ThumbnailDatabase db;
+    FaviconDatabase db;
     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
 
     // Data for PageUrl2() was deleted, but the index entry remains,
@@ -1100,7 +1097,7 @@
 
   // Database should also be recovered at higher levels.
   {
-    ThumbnailDatabase db;
+    FaviconDatabase db;
     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
 
     // Now this fails because there is no mapping.
@@ -1129,7 +1126,7 @@
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);
-    ThumbnailDatabase db;
+    FaviconDatabase db;
     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
 
     EXPECT_FALSE(db.GetIconMappingsForPageURL(PageUrl2(), nullptr));
@@ -1141,8 +1138,8 @@
   }
 }
 
-TEST_F(ThumbnailDatabaseTest, Recovery7) {
-  // Create an example database without loading into ThumbnailDatabase
+TEST_F(FaviconDatabaseTest, Recovery7) {
+  // Create an example database without loading into FaviconDatabase
   // (which would upgrade it).
   EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v7.sql"));
 
@@ -1171,7 +1168,7 @@
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);
-    ThumbnailDatabase db;
+    FaviconDatabase db;
     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
 
     // Data for PageUrl2() was deleted, but the index entry remains,
@@ -1195,7 +1192,7 @@
 
   // Database should also be recovered at higher levels.
   {
-    ThumbnailDatabase db;
+    FaviconDatabase db;
     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
 
     // Now this fails because there is no mapping.
@@ -1224,7 +1221,7 @@
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);
-    ThumbnailDatabase db;
+    FaviconDatabase db;
     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
 
     EXPECT_FALSE(db.GetIconMappingsForPageURL(PageUrl2(), nullptr));
@@ -1236,8 +1233,8 @@
   }
 }
 
-TEST_F(ThumbnailDatabaseTest, Recovery6) {
-  // Create an example database without loading into ThumbnailDatabase
+TEST_F(FaviconDatabaseTest, Recovery6) {
+  // Create an example database without loading into FaviconDatabase
   // (which would upgrade it).
   EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v6.sql"));
 
@@ -1260,7 +1257,7 @@
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);
-    ThumbnailDatabase db;
+    FaviconDatabase db;
     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
     ASSERT_TRUE(expecter.SawExpectedErrors());
   }
@@ -1280,8 +1277,8 @@
   }
 }
 
-TEST_F(ThumbnailDatabaseTest, Recovery5) {
-  // Create an example database without loading into ThumbnailDatabase
+TEST_F(FaviconDatabaseTest, Recovery5) {
+  // Create an example database without loading into FaviconDatabase
   // (which would upgrade it).
   EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v5.sql"));
 
@@ -1304,7 +1301,7 @@
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);
-    ThumbnailDatabase db;
+    FaviconDatabase db;
     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
     ASSERT_TRUE(expecter.SawExpectedErrors());
   }
@@ -1326,13 +1323,13 @@
 
 // Test that various broken schema found in the wild can be opened
 // successfully, and result in the correct schema.
-TEST_F(ThumbnailDatabaseTest, WildSchema) {
+TEST_F(FaviconDatabaseTest, WildSchema) {
   base::FilePath sql_path;
   EXPECT_TRUE(GetTestDataHistoryDir(&sql_path));
   sql_path = sql_path.AppendASCII("thumbnail_wild");
 
-  base::FileEnumerator fe(
-      sql_path, false, base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.sql"));
+  base::FileEnumerator fe(sql_path, false, base::FileEnumerator::FILES,
+                          FILE_PATH_LITERAL("*.sql"));
   for (base::FilePath name = fe.Next(); !name.empty(); name = fe.Next()) {
     SCOPED_TRACE(name.BaseName().AsUTF8Unsafe());
     // Generate a database path based on the golden's basename.
@@ -1344,7 +1341,7 @@
     // All schema flaws should be cleaned up by Init().
     // TODO(shess): Differentiate between databases which need Raze()
     // and those which can be salvaged.
-    ThumbnailDatabase db;
+    FaviconDatabase db;
     ASSERT_EQ(sql::INIT_OK, db.Init(db_path));
 
     // Verify that the resulting schema is correct, whether it
@@ -1353,30 +1350,30 @@
   }
 }
 
-TEST(ThumbnailDatabaseIconTypeTest, ShouldBeBackwardCompatible) {
-  EXPECT_EQ(0, ThumbnailDatabase::ToPersistedIconType(
+TEST(FaviconDatabaseIconTypeTest, ShouldBeBackwardCompatible) {
+  EXPECT_EQ(0, FaviconDatabase::ToPersistedIconType(
                    favicon_base::IconType::kInvalid));
-  EXPECT_EQ(1, ThumbnailDatabase::ToPersistedIconType(
+  EXPECT_EQ(1, FaviconDatabase::ToPersistedIconType(
                    favicon_base::IconType::kFavicon));
-  EXPECT_EQ(2, ThumbnailDatabase::ToPersistedIconType(
+  EXPECT_EQ(2, FaviconDatabase::ToPersistedIconType(
                    favicon_base::IconType::kTouchIcon));
-  EXPECT_EQ(4, ThumbnailDatabase::ToPersistedIconType(
+  EXPECT_EQ(4, FaviconDatabase::ToPersistedIconType(
                    favicon_base::IconType::kTouchPrecomposedIcon));
-  EXPECT_EQ(8, ThumbnailDatabase::ToPersistedIconType(
+  EXPECT_EQ(8, FaviconDatabase::ToPersistedIconType(
                    favicon_base::IconType::kWebManifestIcon));
 
   EXPECT_EQ(favicon_base::IconType::kInvalid,
-            ThumbnailDatabase::FromPersistedIconType(0));
+            FaviconDatabase::FromPersistedIconType(0));
   EXPECT_EQ(favicon_base::IconType::kFavicon,
-            ThumbnailDatabase::FromPersistedIconType(1));
+            FaviconDatabase::FromPersistedIconType(1));
   EXPECT_EQ(favicon_base::IconType::kTouchIcon,
-            ThumbnailDatabase::FromPersistedIconType(2));
+            FaviconDatabase::FromPersistedIconType(2));
   EXPECT_EQ(favicon_base::IconType::kTouchPrecomposedIcon,
-            ThumbnailDatabase::FromPersistedIconType(4));
+            FaviconDatabase::FromPersistedIconType(4));
   EXPECT_EQ(favicon_base::IconType::kWebManifestIcon,
-            ThumbnailDatabase::FromPersistedIconType(8));
+            FaviconDatabase::FromPersistedIconType(8));
   EXPECT_EQ(favicon_base::IconType::kInvalid,
-            ThumbnailDatabase::FromPersistedIconType(16));
+            FaviconDatabase::FromPersistedIconType(16));
 }
 
 }  // namespace history
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index 99dc4a2..140c974 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -776,7 +776,7 @@
 #endif
 
   base::FilePath history_name = history_dir_.Append(kHistoryFilename);
-  base::FilePath thumbnail_name = GetFaviconsFileName();
+  base::FilePath favicon_name = GetFaviconsFileName();
 
   // Delete the old index database files which are no longer used.
   DeleteFTSIndexDatabases();
@@ -829,37 +829,32 @@
   }
   db_->BeginExclusiveMode();  // Must be after the mem backend read the data.
 
-  // Thumbnail database.
-  // TODO(shess): "thumbnail database" these days only stores
-  // favicons.  Thumbnails are stored in "top sites".  Consider
-  // renaming "thumbnail" references to "favicons" or something of the
-  // sort.
-  thumbnail_db_ = std::make_unique<ThumbnailDatabase>();
-  if (thumbnail_db_->Init(thumbnail_name) != sql::INIT_OK) {
+  // Favicon database.
+  favicon_db_ = std::make_unique<FaviconDatabase>();
+  if (favicon_db_->Init(favicon_name) != sql::INIT_OK) {
     // Unlike the main database, we don't error out when the database is too
     // new because this error is much less severe. Generally, this shouldn't
-    // happen since the thumbnail and main database versions should be in sync.
-    // We'll just continue without thumbnails & favicons in this case or any
-    // other error.
-    LOG(WARNING) << "Could not initialize the thumbnail database.";
-    thumbnail_db_.reset();
+    // happen since the favicon and main database versions should be in sync.
+    // We'll just continue without favicons in this case or any other error.
+    LOG(WARNING) << "Could not initialize the favicon database.";
+    favicon_db_.reset();
   }
 
-  // Generate the history and thumbnail database metrics only after performing
+  // Generate the history and favicon database metrics only after performing
   // any migration work.
   if (base::RandInt(1, 100) == 50) {
     // Only do this computation sometimes since it can be expensive.
     db_->ComputeDatabaseMetrics(history_name);
-    if (thumbnail_db_)
-      thumbnail_db_->ComputeDatabaseMetrics();
+    if (favicon_db_)
+      favicon_db_->ComputeDatabaseMetrics();
   }
 
-  expirer_.SetDatabases(db_.get(), thumbnail_db_.get());
+  expirer_.SetDatabases(db_.get(), favicon_db_.get());
 
   // Open the long-running transaction.
   db_->BeginTransaction();
-  if (thumbnail_db_)
-    thumbnail_db_->BeginTransaction();
+  if (favicon_db_)
+    favicon_db_->BeginTransaction();
 
   // Get the first item in our database.
   db_->GetStartDate(&first_recorded_time_);
@@ -870,7 +865,7 @@
 #if defined(OS_ANDROID)
   if (backend_client_) {
     backend_client_->OnHistoryBackendInitialized(
-        this, db_.get(), thumbnail_db_.get(), history_dir_);
+        this, db_.get(), favicon_db_.get(), history_dir_);
   }
 #endif
 
@@ -887,8 +882,8 @@
   }
   if (db_)
     db_->TrimMemory();
-  if (thumbnail_db_)
-    thumbnail_db_->TrimMemory();
+  if (favicon_db_)
+    favicon_db_->TrimMemory();
 }
 
 void HistoryBackend::CloseAllDatabases() {
@@ -899,9 +894,9 @@
     // Forget the first recorded time since the database is closed.
     first_recorded_time_ = base::Time();
   }
-  if (thumbnail_db_) {
-    thumbnail_db_->CommitTransaction();
-    thumbnail_db_.reset();
+  if (favicon_db_) {
+    favicon_db_->CommitTransaction();
+    favicon_db_.reset();
   }
 }
 
@@ -1680,13 +1675,13 @@
     const GURL& page_url,
     const std::vector<favicon_base::IconTypeSet>& icon_types_list,
     int minimum_size_in_pixels) {
-  if (!db_ || !thumbnail_db_)
+  if (!db_ || !favicon_db_)
     return {};
 
   TimeTicks beginning_time = TimeTicks::Now();
 
   std::vector<IconMapping> icon_mappings;
-  if (!thumbnail_db_->GetIconMappingsForPageURL(page_url, &icon_mappings) ||
+  if (!favicon_db_->GetIconMappingsForPageURL(page_url, &icon_mappings) ||
       icon_mappings.empty())
     return {};
 
@@ -1702,7 +1697,7 @@
     if (required_icon_types.count(i->icon_type) == 0)
       continue;
     std::vector<FaviconBitmapIDSize> bitmap_id_sizes;
-    thumbnail_db_->GetFaviconBitmapIDSizes(i->icon_id, &bitmap_id_sizes);
+    favicon_db_->GetFaviconBitmapIDSizes(i->icon_id, &bitmap_id_sizes);
     FaviconBitmap& largest = largest_favicon_bitmaps[i->icon_type];
     for (std::vector<FaviconBitmapIDSize>::const_iterator j =
              bitmap_id_sizes.begin();
@@ -1740,8 +1735,8 @@
 
   GURL icon_url;
   favicon_base::IconType icon_type;
-  if (!thumbnail_db_->GetFaviconHeader(largest_icon.icon_id, &icon_url,
-                                       &icon_type)) {
+  if (!favicon_db_->GetFaviconHeader(largest_icon.icon_id, &icon_url,
+                                     &icon_type)) {
     return {};
   }
 
@@ -1750,7 +1745,7 @@
   favicon_base::FaviconRawBitmapResult bitmap_result;
   bitmap_result.icon_url = icon_url;
   bitmap_result.icon_type = icon_type;
-  if (!thumbnail_db_->GetFaviconBitmap(
+  if (!favicon_db_->GetFaviconBitmap(
           largest_icon.bitmap_id, &last_updated, &last_requested,
           &bitmap_result.bitmap_data, &bitmap_result.pixel_size)) {
     return {};
@@ -1822,7 +1817,7 @@
     favicon_base::IconType icon_type) {
   TRACE_EVENT0("browser", "HistoryBackend::DeleteFaviconMappings");
 
-  if (!thumbnail_db_ || !db_)
+  if (!favicon_db_ || !db_)
     return;
 
   for (const GURL& page_url : page_urls) {
@@ -1845,19 +1840,19 @@
     const gfx::Size& pixel_size) {
   TRACE_EVENT0("browser", "HistoryBackend::MergeFavicon");
 
-  if (!thumbnail_db_ || !db_)
+  if (!favicon_db_ || !db_)
     return;
 
   favicon_base::FaviconID favicon_id =
-      thumbnail_db_->GetFaviconIDForFaviconURL(icon_url, icon_type);
+      favicon_db_->GetFaviconIDForFaviconURL(icon_url, icon_type);
 
   if (!favicon_id) {
     // There is no favicon at |icon_url|, create it.
-    favicon_id = thumbnail_db_->AddFavicon(icon_url, icon_type);
+    favicon_id = favicon_db_->AddFavicon(icon_url, icon_type);
   }
 
   std::vector<FaviconBitmapIDSize> bitmap_id_sizes;
-  thumbnail_db_->GetFaviconBitmapIDSizes(favicon_id, &bitmap_id_sizes);
+  favicon_db_->GetFaviconBitmapIDSizes(favicon_id, &bitmap_id_sizes);
 
   // If there is already a favicon bitmap of |pixel_size| at |icon_url|,
   // replace it.
@@ -1879,8 +1874,8 @@
         // favicon bitmap causes it to be redownloaded the next time that the
         // user visits any page which uses |icon_url|. It also allows storing an
         // on-demand icon along with the icon from sync.
-        thumbnail_db_->SetFaviconBitmap(bitmap_id_sizes[i].bitmap_id,
-                                        bitmap_data, base::Time());
+        favicon_db_->SetFaviconBitmap(bitmap_id_sizes[i].bitmap_id, bitmap_data,
+                                      base::Time());
         replaced_bitmap = true;
       }
       break;
@@ -1896,12 +1891,12 @@
   if (!replaced_bitmap && !bitmap_identical) {
     // Set the preexisting favicon bitmaps as expired as the preexisting favicon
     // bitmaps are not consistent with the merged in data.
-    thumbnail_db_->SetFaviconOutOfDate(favicon_id);
+    favicon_db_->SetFaviconOutOfDate(favicon_id);
 
     // Delete an arbitrary favicon bitmap to avoid going over the limit of
     // |kMaxFaviconBitmapsPerIconURL|.
     if (bitmap_id_sizes.size() >= kMaxFaviconBitmapsPerIconURL) {
-      thumbnail_db_->DeleteFaviconBitmap(bitmap_id_sizes[0].bitmap_id);
+      favicon_db_->DeleteFaviconBitmap(bitmap_id_sizes[0].bitmap_id);
       favicon_sizes.erase(favicon_sizes.begin());
     }
     // Set the new bitmap as expired because the bitmaps from sync/profile
@@ -1909,9 +1904,9 @@
     // bitmaps to be redownloaded the next time that the user visits any page
     // which uses |icon_url|. It also allows storing an on-demand icon along
     // with the icon from sync.
-    thumbnail_db_->AddFaviconBitmap(favicon_id, bitmap_data,
-                                    FaviconBitmapType::ON_VISIT, base::Time(),
-                                    pixel_size);
+    favicon_db_->AddFaviconBitmap(favicon_id, bitmap_data,
+                                  FaviconBitmapType::ON_VISIT, base::Time(),
+                                  pixel_size);
     favicon_sizes.push_back(pixel_size);
   }
 
@@ -1944,8 +1939,7 @@
   //               modified.
 
   std::vector<IconMapping> icon_mappings;
-  thumbnail_db_->GetIconMappingsForPageURL(page_url, {icon_type},
-                                           &icon_mappings);
+  favicon_db_->GetIconMappingsForPageURL(page_url, {icon_type}, &icon_mappings);
 
   // Copy the favicon bitmaps mapped to |page_url| to the favicon at |icon_url|
   // till the limit of |kMaxFaviconBitmapsPerIconURL| is reached.
@@ -1958,8 +1952,7 @@
       continue;
 
     std::vector<FaviconBitmap> bitmaps_to_copy;
-    thumbnail_db_->GetFaviconBitmaps(icon_mappings[i].icon_id,
-                                     &bitmaps_to_copy);
+    favicon_db_->GetFaviconBitmaps(icon_mappings[i].icon_id, &bitmaps_to_copy);
     for (size_t j = 0; j < bitmaps_to_copy.size(); ++j) {
       // Do not add a favicon bitmap at a pixel size for which there is already
       // a favicon bitmap mapped to |icon_url|. The one there is more correct
@@ -1970,10 +1963,9 @@
 
       // Add the favicon bitmap as expired as it is not consistent with the
       // merged in data.
-      thumbnail_db_->AddFaviconBitmap(favicon_id,
-                                      bitmaps_to_copy[j].bitmap_data,
-                                      FaviconBitmapType::ON_VISIT, base::Time(),
-                                      bitmaps_to_copy[j].pixel_size);
+      favicon_db_->AddFaviconBitmap(favicon_id, bitmaps_to_copy[j].bitmap_data,
+                                    FaviconBitmapType::ON_VISIT, base::Time(),
+                                    bitmaps_to_copy[j].pixel_size);
       favicon_sizes.push_back(bitmaps_to_copy[j].pixel_size);
       favicon_bitmaps_copied = true;
 
@@ -2012,7 +2004,7 @@
     const base::flat_set<GURL>& page_urls_to_write) {
   TRACE_EVENT0("browser", "HistoryBackend::CloneFaviconMappingsForPages");
 
-  if (!db_ || !thumbnail_db_)
+  if (!db_ || !favicon_db_)
     return;
 
   // Update mappings including redirects for each entry in |page_urls_to_write|.
@@ -2031,8 +2023,8 @@
 
   // Get FaviconIDs for |page_url_to_read| and one of |icon_types|.
   std::vector<IconMapping> icon_mappings;
-  thumbnail_db_->GetIconMappingsForPageURL(page_url_to_read, icon_types,
-                                           &icon_mappings);
+  favicon_db_->GetIconMappingsForPageURL(page_url_to_read, icon_types,
+                                         &icon_mappings);
   if (icon_mappings.empty())
     return;
 
@@ -2053,7 +2045,7 @@
 
 bool HistoryBackend::CanSetOnDemandFavicons(const GURL& page_url,
                                             favicon_base::IconType icon_type) {
-  if (!thumbnail_db_ || !db_)
+  if (!favicon_db_ || !db_)
     return false;
 
   // We allow writing an on demand favicon of type |icon_type| only if there is
@@ -2062,15 +2054,15 @@
   // icon if there is only an icon from sync (icons from sync are immediately
   // set as expired).
   std::vector<IconMapping> mapping_data;
-  thumbnail_db_->GetIconMappingsForPageURL(page_url, &mapping_data);
+  favicon_db_->GetIconMappingsForPageURL(page_url, &mapping_data);
 
   for (const IconMapping& mapping : mapping_data) {
     if (AreIconTypesEquivalent(mapping.icon_type, icon_type))
       return false;
 
     base::Time last_updated;
-    if (thumbnail_db_->GetFaviconLastUpdatedTime(mapping.icon_id,
-                                                 &last_updated) &&
+    if (favicon_db_->GetFaviconLastUpdatedTime(mapping.icon_id,
+                                               &last_updated) &&
         !IsFaviconBitmapExpired(last_updated)) {
       return false;
     }
@@ -2094,12 +2086,12 @@
 
   std::vector<IconMapping> icon_mappings;
 
-  if (!thumbnail_db_ ||
-      !thumbnail_db_->GetIconMappingsForPageURL(page_url, &icon_mappings))
+  if (!favicon_db_ ||
+      !favicon_db_->GetIconMappingsForPageURL(page_url, &icon_mappings))
     return;
 
   for (auto m = icon_mappings.begin(); m != icon_mappings.end(); ++m) {
-    thumbnail_db_->SetFaviconOutOfDate(m->icon_id);
+    favicon_db_->SetFaviconOutOfDate(m->icon_id);
   }
   ScheduleCommit();
 }
@@ -2107,10 +2099,10 @@
 void HistoryBackend::TouchOnDemandFavicon(const GURL& icon_url) {
   TRACE_EVENT0("browser", "HistoryBackend::TouchOnDemandFavicon");
 
-  if (!thumbnail_db_)
+  if (!favicon_db_)
     return;
 
-  thumbnail_db_->TouchOnDemandFavicon(icon_url, Time::Now());
+  favicon_db_->TouchOnDemandFavicon(icon_url, Time::Now());
   ScheduleCommit();
 }
 
@@ -2118,7 +2110,7 @@
     const favicon_base::FaviconUsageDataList& favicon_usage) {
   TRACE_EVENT0("browser", "HistoryBackend::SetImportedFavicons");
 
-  if (!db_ || !thumbnail_db_)
+  if (!db_ || !favicon_db_)
     return;
 
   Time now = Time::Now();
@@ -2127,13 +2119,12 @@
   std::set<GURL> favicons_changed;
 
   for (size_t i = 0; i < favicon_usage.size(); i++) {
-    favicon_base::FaviconID favicon_id =
-        thumbnail_db_->GetFaviconIDForFaviconURL(
-            favicon_usage[i].favicon_url, favicon_base::IconType::kFavicon);
+    favicon_base::FaviconID favicon_id = favicon_db_->GetFaviconIDForFaviconURL(
+        favicon_usage[i].favicon_url, favicon_base::IconType::kFavicon);
     if (!favicon_id) {
       // This favicon doesn't exist yet, so we create it using the given data.
       // TODO(pkotwicz): Pass in real pixel size.
-      favicon_id = thumbnail_db_->AddFavicon(
+      favicon_id = favicon_db_->AddFavicon(
           favicon_usage[i].favicon_url, favicon_base::IconType::kFavicon,
           new base::RefCountedBytes(favicon_usage[i].png_data),
           FaviconBitmapType::ON_VISIT, now, gfx::Size());
@@ -2157,16 +2148,16 @@
           url_info.set_last_visit(base::Time());
           url_info.set_hidden(false);
           db_->AddURL(url_info);
-          thumbnail_db_->AddIconMapping(*url, favicon_id);
+          favicon_db_->AddIconMapping(*url, favicon_id);
           favicons_changed.insert(*url);
         }
       } else {
-        if (!thumbnail_db_->GetIconMappingsForPageURL(
+        if (!favicon_db_->GetIconMappingsForPageURL(
                 *url, {favicon_base::IconType::kFavicon},
                 /*mapping_data=*/nullptr)) {
           // URL is present in history, update the favicon *only* if it is not
           // set already.
-          thumbnail_db_->AddIconMapping(*url, favicon_id);
+          favicon_db_->AddIconMapping(*url, favicon_id);
           favicons_changed.insert(*url);
         }
       }
@@ -2186,13 +2177,13 @@
                                      FaviconBitmapType type) {
   DCHECK(!page_urls.empty());
 
-  if (!thumbnail_db_ || !db_)
+  if (!favicon_db_ || !db_)
     return false;
 
   DCHECK_GE(kMaxFaviconBitmapsPerIconURL, bitmaps.size());
 
   favicon_base::FaviconID icon_id =
-      thumbnail_db_->GetFaviconIDForFaviconURL(icon_url, icon_type);
+      favicon_db_->GetFaviconIDForFaviconURL(icon_url, icon_type);
 
   if (type == FaviconBitmapType::ON_DEMAND) {
     UMA_HISTOGRAM_BOOLEAN("Favicon.OnDemandIconExistsInDb",
@@ -2201,7 +2192,7 @@
 
   bool favicon_created = false;
   if (!icon_id) {
-    icon_id = thumbnail_db_->AddFavicon(icon_url, icon_type);
+    icon_id = favicon_db_->AddFavicon(icon_url, icon_type);
     favicon_created = true;
   }
 
@@ -2236,11 +2227,11 @@
     std::vector<favicon_base::FaviconRawBitmapResult>* bitmap_results) {
   bitmap_results->clear();
 
-  if (!thumbnail_db_)
+  if (!favicon_db_)
     return;
 
   const favicon_base::FaviconID favicon_id =
-      thumbnail_db_->GetFaviconIDForFaviconURL(icon_url, icon_type);
+      favicon_db_->GetFaviconIDForFaviconURL(icon_url, icon_type);
   if (!favicon_id)
     return;
 
@@ -2261,7 +2252,7 @@
                                        const std::vector<SkBitmap>& bitmaps,
                                        FaviconBitmapType type) {
   std::vector<FaviconBitmapIDSize> bitmap_id_sizes;
-  thumbnail_db_->GetFaviconBitmapIDSizes(icon_id, &bitmap_id_sizes);
+  favicon_db_->GetFaviconBitmapIDSizes(icon_id, &bitmap_id_sizes);
 
   using PNGEncodedBitmap =
       std::pair<scoped_refptr<base::RefCountedBytes>, gfx::Size>;
@@ -2289,16 +2280,16 @@
 
     FaviconBitmapID bitmap_id = bitmap_id_sizes[i].bitmap_id;
     if (match_it == to_add.end()) {
-      thumbnail_db_->DeleteFaviconBitmap(bitmap_id);
+      favicon_db_->DeleteFaviconBitmap(bitmap_id);
 
       favicon_bitmaps_changed = true;
     } else {
       if (!favicon_bitmaps_changed &&
           IsFaviconBitmapDataEqual(bitmap_id, match_it->first)) {
-        thumbnail_db_->SetFaviconBitmapLastUpdateTime(
+        favicon_db_->SetFaviconBitmapLastUpdateTime(
             bitmap_id, base::Time::Now() /* new last updated time */);
       } else {
-        thumbnail_db_->SetFaviconBitmap(
+        favicon_db_->SetFaviconBitmap(
             bitmap_id, match_it->first,
             base::Time::Now() /* new last updated time */);
         favicon_bitmaps_changed = true;
@@ -2308,7 +2299,7 @@
   }
 
   for (size_t i = 0; i < to_add.size(); ++i) {
-    thumbnail_db_->AddFaviconBitmap(
+    favicon_db_->AddFaviconBitmap(
         icon_id, to_add[i].first, type,
         base::Time::Now() /* new last updated / last requested time */,
         to_add[i].second);
@@ -2325,8 +2316,8 @@
     return false;
 
   scoped_refptr<base::RefCountedMemory> original_bitmap_data;
-  thumbnail_db_->GetFaviconBitmap(bitmap_id, nullptr, nullptr,
-                                  &original_bitmap_data, nullptr);
+  favicon_db_->GetFaviconBitmap(bitmap_id, nullptr, nullptr,
+                                &original_bitmap_data, nullptr);
   return new_bitmap_data->Equals(original_bitmap_data);
 }
 
@@ -2339,7 +2330,7 @@
   DCHECK(favicon_bitmap_results);
   favicon_bitmap_results->clear();
 
-  if (!db_ || !thumbnail_db_)
+  if (!db_ || !favicon_db_)
     return false;
 
   // Time the query.
@@ -2347,8 +2338,7 @@
 
   // Get FaviconIDs for |page_url| and one of |icon_types|.
   std::vector<IconMapping> icon_mappings;
-  thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_types,
-                                           &icon_mappings);
+  favicon_db_->GetIconMappingsForPageURL(page_url, icon_types, &icon_mappings);
 
   if (icon_mappings.empty() && fallback_to_host &&
       page_url.SchemeIsHTTPOrHTTPS()) {
@@ -2357,11 +2347,11 @@
     // that is known to exist and matches the host of |page_url|. Do this only
     // if we have a HTTP/HTTPS url.
     base::Optional<GURL> fallback_page_url =
-        thumbnail_db_->FindFirstPageURLForHost(page_url, icon_types);
+        favicon_db_->FindFirstPageURLForHost(page_url, icon_types);
 
     if (fallback_page_url) {
-      thumbnail_db_->GetIconMappingsForPageURL(fallback_page_url.value(),
-                                               icon_types, &icon_mappings);
+      favicon_db_->GetIconMappingsForPageURL(fallback_page_url.value(),
+                                             icon_types, &icon_mappings);
     }
   }
 
@@ -2395,8 +2385,8 @@
   float highest_score = kSelectFaviconFramesInvalidScore;
   for (size_t i = 0; i < candidate_favicon_ids.size(); ++i) {
     std::vector<FaviconBitmapIDSize> bitmap_id_sizes;
-    thumbnail_db_->GetFaviconBitmapIDSizes(candidate_favicon_ids[i],
-                                           &bitmap_id_sizes);
+    favicon_db_->GetFaviconBitmapIDSizes(candidate_favicon_ids[i],
+                                         &bitmap_id_sizes);
 
     // Build vector of gfx::Size from |bitmap_id_sizes|.
     std::vector<gfx::Size> sizes;
@@ -2422,8 +2412,7 @@
   // |best_bitmap_ids|.
   GURL icon_url;
   favicon_base::IconType icon_type;
-  if (!thumbnail_db_->GetFaviconHeader(best_favicon_id, &icon_url,
-                                       &icon_type)) {
+  if (!favicon_db_->GetFaviconHeader(best_favicon_id, &icon_url, &icon_type)) {
     return false;
   }
 
@@ -2433,7 +2422,7 @@
     favicon_base::FaviconRawBitmapResult bitmap_result;
     bitmap_result.icon_url = icon_url;
     bitmap_result.icon_type = icon_type;
-    if (!thumbnail_db_->GetFaviconBitmap(
+    if (!favicon_db_->GetFaviconBitmap(
             best_bitmap_ids[i], &last_updated, &last_requested,
             &bitmap_result.bitmap_data, &bitmap_result.pixel_size)) {
       return false;
@@ -2452,7 +2441,7 @@
     const GURL& page_url,
     favicon_base::IconType icon_type,
     favicon_base::FaviconID icon_id) {
-  if (!thumbnail_db_)
+  if (!favicon_db_)
     return false;
 
   // Find all the pages whose favicons we should set, we want to set it for
@@ -2492,7 +2481,7 @@
   favicon_base::FaviconID unmapped_icon_id = icon_id;
 
   std::vector<IconMapping> icon_mappings;
-  thumbnail_db_->GetIconMappingsForPageURL(page_url, &icon_mappings);
+  favicon_db_->GetIconMappingsForPageURL(page_url, &icon_mappings);
 
   for (auto m = icon_mappings.begin(); m != icon_mappings.end(); ++m) {
     if (unmapped_icon_id == m->icon_id) {
@@ -2501,20 +2490,20 @@
     }
 
     if (AreIconTypesEquivalent(icon_type, m->icon_type)) {
-      thumbnail_db_->DeleteIconMapping(m->mapping_id);
+      favicon_db_->DeleteIconMapping(m->mapping_id);
 
       // Removing the icon mapping may have orphaned the associated favicon so
       // we must recheck it. This is not super fast, but this case will get
       // triggered rarely, since normally a page will always map to the same
       // favicon IDs. It will mostly happen for favicons we import.
-      if (!thumbnail_db_->HasMappingFor(m->icon_id))
-        thumbnail_db_->DeleteFavicon(m->icon_id);
+      if (!favicon_db_->HasMappingFor(m->icon_id))
+        favicon_db_->DeleteFavicon(m->icon_id);
       mappings_changed = true;
     }
   }
 
   if (unmapped_icon_id) {
-    thumbnail_db_->AddIconMapping(page_url, unmapped_icon_id);
+    favicon_db_->AddIconMapping(page_url, unmapped_icon_id);
     mappings_changed = true;
   }
   return mappings_changed;
@@ -2570,11 +2559,11 @@
       << "Somebody left a transaction open";
   db_->BeginTransaction();
 
-  if (thumbnail_db_) {
-    thumbnail_db_->CommitTransaction();
-    DCHECK_EQ(thumbnail_db_->transaction_nesting(), 0)
+  if (favicon_db_) {
+    favicon_db_->CommitTransaction();
+    DCHECK_EQ(favicon_db_->transaction_nesting(), 0)
         << "Somebody left a transaction open";
-    thumbnail_db_->BeginTransaction();
+    favicon_db_->BeginTransaction();
   }
 }
 
@@ -2938,8 +2927,8 @@
   }
 
   // Delete all cached favicons which are not used by the UI.
-  if (!ClearAllThumbnailHistory(starred_urls)) {
-    LOG(ERROR) << "Thumbnail history could not be cleared";
+  if (!ClearAllFaviconHistory(starred_urls)) {
+    LOG(ERROR) << "Favicon history could not be cleared";
     // We continue in this error case. If the user wants to delete their
     // history, we should delete as much as we can.
   }
@@ -2958,10 +2947,10 @@
   NotifyURLsDeleted(DeletionInfo::ForAllHistory());
 }
 
-bool HistoryBackend::ClearAllThumbnailHistory(
+bool HistoryBackend::ClearAllFaviconHistory(
     const std::vector<GURL>& kept_urls) {
-  if (!thumbnail_db_) {
-    // When we have no reference to the thumbnail database, maybe there was an
+  if (!favicon_db_) {
+    // When we have no reference to the favicon database, maybe there was an
     // error opening it. In this case, we just try to blow it away to try to
     // fix the error if it exists. This may fail, in which case either the
     // file doesn't exist or there's no more we can do.
@@ -2970,14 +2959,14 @@
   }
 
   // Isolate from any long-running transaction.
-  thumbnail_db_->CommitTransaction();
-  thumbnail_db_->BeginTransaction();
+  favicon_db_->CommitTransaction();
+  favicon_db_->BeginTransaction();
 
   // TODO(shess): If this fails, perhaps the database should be razed
   // or deleted.
-  if (!thumbnail_db_->RetainDataForPageUrls(kept_urls)) {
-    thumbnail_db_->RollbackTransaction();
-    thumbnail_db_->BeginTransaction();
+  if (!favicon_db_->RetainDataForPageUrls(kept_urls)) {
+    favicon_db_->RollbackTransaction();
+    favicon_db_->BeginTransaction();
     return false;
   }
 
@@ -2990,10 +2979,10 @@
   // Vacuum to remove all the pages associated with the dropped tables. There
   // must be no transaction open on the table when we do this. We assume that
   // our long-running transaction is open, so we complete it and start it again.
-  DCHECK_EQ(thumbnail_db_->transaction_nesting(), 1);
-  thumbnail_db_->CommitTransaction();
-  thumbnail_db_->Vacuum();
-  thumbnail_db_->BeginTransaction();
+  DCHECK_EQ(favicon_db_->transaction_nesting(), 1);
+  favicon_db_->CommitTransaction();
+  favicon_db_->Vacuum();
+  favicon_db_->BeginTransaction();
   return true;
 }
 
diff --git a/components/history/core/browser/history_backend.h b/components/history/core/browser/history_backend.h
index 6e17f71..b203a89 100644
--- a/components/history/core/browser/history_backend.h
+++ b/components/history/core/browser/history_backend.h
@@ -30,10 +30,10 @@
 #include "build/build_config.h"
 #include "components/favicon_base/favicon_usage_data.h"
 #include "components/history/core/browser/expire_history_backend.h"
+#include "components/history/core/browser/favicon_database.h"
 #include "components/history/core/browser/history_backend_notifier.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/history/core/browser/keyword_id.h"
-#include "components/history/core/browser/thumbnail_database.h"
 #include "components/history/core/browser/visit_tracker.h"
 #include "sql/init_status.h"
 
@@ -64,7 +64,7 @@
 class URLDatabase;
 
 // The maximum number of bitmaps for a single icon URL which can be stored in
-// the thumbnail database.
+// the favicon database.
 static const size_t kMaxFaviconBitmapsPerIconURL = 8;
 
 // Returns a formatted version of |url| with the HTTP/HTTPS scheme, port,
@@ -216,7 +216,7 @@
 
   void ClearCachedDataForContextID(ContextID context_id);
 
-  // Clears all on-demand favicons from thumbnail database.
+  // Clears all on-demand favicons.
   void ClearAllOnDemandFavicons();
 
   // Gets the counts and last last time of URLs that belong to |origins| in the
@@ -554,7 +554,6 @@
   FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, ImportedFaviconsTest);
   FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, URLsNoLongerBookmarked);
   FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, StripUsernamePasswordTest);
-  FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, DeleteThumbnailsDatabaseTest);
   FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, AddPageVisitSource);
   FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, AddPageVisitBackForward);
   FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, AddPageVisitRedirectBackForward);
@@ -634,8 +633,7 @@
                            ProcessUserChangeRemove);
   friend class ::TestingProfile;
 
-  // Returns the name of the Favicons database. This is the new name
-  // of the Thumbnails database.
+  // Returns the name of the Favicons database.
   base::FilePath GetFaviconsFileName() const;
 
   class URLQuerier;
@@ -868,15 +866,14 @@
   // Deletes all history. This is a special case of deleting that is separated
   // from our normal dependency-following method for performance reasons. The
   // logic lives here instead of ExpireHistoryBackend since it will cause
-  // re-initialization of some databases (e.g. Thumbnails) that could fail.
+  // re-initialization of some databases (e.g. favicons) that could fail.
   // When these databases are not valid, our pointers must be null, so we need
   // to handle this type of operation to keep the pointers in sync.
   void DeleteAllHistory();
 
-  // Given a vector of all URLs that we will keep, removes all thumbnails
-  // referenced by any URL, and also all favicons that aren't used by those
-  // URLs.
-  bool ClearAllThumbnailHistory(const std::vector<GURL>& kept_urls);
+  // Given a vector of all URLs that we will keep, removes all favicons that
+  // aren't used by those URLs.
+  bool ClearAllFaviconHistory(const std::vector<GURL>& kept_urls);
 
   // Deletes all information in the history database, except for the supplied
   // set of URLs in the URL table (these should correspond to the bookmarked
@@ -898,13 +895,13 @@
   // Directory where database files will be stored, empty until Init is called.
   base::FilePath history_dir_;
 
-  // The history/thumbnail databases. Either may be null if the database could
+  // The history/favicon databases. Either may be null if the database could
   // not be opened, all users must first check for null and return immediately
-  // if it is. The thumbnail DB may be null when the history one isn't, but not
+  // if it is. The favicon DB may be null when the history one isn't, but not
   // vice-versa.
   std::unique_ptr<HistoryDatabase> db_;
   bool scheduled_kill_db_;  // Database is being killed due to error.
-  std::unique_ptr<ThumbnailDatabase> thumbnail_db_;
+  std::unique_ptr<FaviconDatabase> favicon_db_;
 
   // Manages expiration between the various databases.
   ExpireHistoryBackend expirer_;
diff --git a/components/history/core/browser/history_backend_client.h b/components/history/core/browser/history_backend_client.h
index 343f722..b6b7136 100644
--- a/components/history/core/browser/history_backend_client.h
+++ b/components/history/core/browser/history_backend_client.h
@@ -20,7 +20,7 @@
 
 class HistoryBackend;
 class HistoryDatabase;
-class ThumbnailDatabase;
+class FaviconDatabase;
 
 struct URLAndTitle {
   GURL url;
@@ -48,7 +48,7 @@
   virtual void OnHistoryBackendInitialized(
       HistoryBackend* history_backend,
       HistoryDatabase* history_database,
-      ThumbnailDatabase* thumbnail_database,
+      FaviconDatabase* favicon_database,
       const base::FilePath& history_dir) = 0;
 
   // Called upon destruction of the HistoryBackend.
diff --git a/components/history/core/browser/history_backend_unittest.cc b/components/history/core/browser/history_backend_unittest.cc
index cfcea3a..d81b44d 100644
--- a/components/history/core/browser/history_backend_unittest.cc
+++ b/components/history/core/browser/history_backend_unittest.cc
@@ -409,16 +409,15 @@
   // Returns the number of icon mappings of |icon_type| to |page_url|.
   size_t NumIconMappingsForPageURL(const GURL& page_url, IconType icon_type) {
     std::vector<IconMapping> icon_mappings;
-    backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, {icon_type},
-                                                       &icon_mappings);
+    backend_->favicon_db_->GetIconMappingsForPageURL(page_url, {icon_type},
+                                                     &icon_mappings);
     return icon_mappings.size();
   }
 
   // Returns the icon mappings for |page_url|.
   std::vector<IconMapping> GetIconMappingsForPageURL(const GURL& page_url) {
     std::vector<IconMapping> icon_mappings;
-    backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
-                                                       &icon_mappings);
+    backend_->favicon_db_->GetIconMappingsForPageURL(page_url, &icon_mappings);
     return icon_mappings;
   }
 
@@ -426,7 +425,7 @@
   // ascending order. Returns true if there is at least one favicon bitmap.
   bool GetSortedFaviconBitmaps(favicon_base::FaviconID icon_id,
                                std::vector<FaviconBitmap>* favicon_bitmaps) {
-    if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, favicon_bitmaps))
+    if (!backend_->favicon_db_->GetFaviconBitmaps(icon_id, favicon_bitmaps))
       return false;
     std::sort(
         favicon_bitmaps->begin(), favicon_bitmaps->end(),
@@ -441,7 +440,7 @@
   bool GetOnlyFaviconBitmap(const favicon_base::FaviconID icon_id,
                             FaviconBitmap* favicon_bitmap) {
     std::vector<FaviconBitmap> favicon_bitmaps;
-    if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, &favicon_bitmaps))
+    if (!backend_->favicon_db_->GetFaviconBitmaps(icon_id, &favicon_bitmaps))
       return false;
     if (favicon_bitmaps.size() != 1)
       return false;
@@ -574,26 +573,26 @@
   GURL favicon_url1("http://www.google.com/favicon.ico");
   GURL favicon_url2("http://news.google.com/favicon.ico");
   favicon_base::FaviconID favicon2 =
-      backend_->thumbnail_db_->AddFavicon(favicon_url2, IconType::kFavicon);
+      backend_->favicon_db_->AddFavicon(favicon_url2, IconType::kFavicon);
   favicon_base::FaviconID favicon1 =
-      backend_->thumbnail_db_->AddFavicon(favicon_url1, IconType::kFavicon);
+      backend_->favicon_db_->AddFavicon(favicon_url1, IconType::kFavicon);
 
   std::vector<unsigned char> data;
   data.push_back('a');
-  EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(
+  EXPECT_TRUE(backend_->favicon_db_->AddFaviconBitmap(
       favicon1, new base::RefCountedBytes(data), FaviconBitmapType::ON_VISIT,
       base::Time::Now(), kSmallSize));
   data[0] = 'b';
-  EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(
+  EXPECT_TRUE(backend_->favicon_db_->AddFaviconBitmap(
       favicon1, new base::RefCountedBytes(data), FaviconBitmapType::ON_VISIT,
       base::Time::Now(), kLargeSize));
 
   data[0] = 'c';
-  EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(
+  EXPECT_TRUE(backend_->favicon_db_->AddFaviconBitmap(
       favicon2, new base::RefCountedBytes(data), FaviconBitmapType::ON_VISIT,
       base::Time::Now(), kSmallSize));
   data[0] = 'd';
-  EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(
+  EXPECT_TRUE(backend_->favicon_db_->AddFaviconBitmap(
       favicon2, new base::RefCountedBytes(data), FaviconBitmapType::ON_VISIT,
       base::Time::Now(), kLargeSize));
 
@@ -602,12 +601,12 @@
   row1.set_visit_count(2);
   row1.set_typed_count(1);
   row1.set_last_visit(base::Time::Now());
-  backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1);
+  backend_->favicon_db_->AddIconMapping(row1.url(), favicon1);
 
   URLRow row2(GURL("http://news.google.com/"));
   row2.set_visit_count(1);
   row2.set_last_visit(base::Time::Now());
-  backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2);
+  backend_->favicon_db_->AddIconMapping(row2.url(), favicon2);
 
   URLRows rows;
   rows.push_back(row2);  // Reversed order for the same reason as favicons.
@@ -659,13 +658,13 @@
   // We should have a favicon and favicon bitmaps for the first URL only. We
   // look them up by favicon URL since the IDs may have changed.
   favicon_base::FaviconID out_favicon1 =
-      backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url1,
-                                                         IconType::kFavicon);
+      backend_->favicon_db_->GetFaviconIDForFaviconURL(favicon_url1,
+                                                       IconType::kFavicon);
   EXPECT_TRUE(out_favicon1);
 
   std::vector<FaviconBitmap> favicon_bitmaps;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
-      out_favicon1, &favicon_bitmaps));
+  EXPECT_TRUE(
+      backend_->favicon_db_->GetFaviconBitmaps(out_favicon1, &favicon_bitmaps));
   ASSERT_EQ(2u, favicon_bitmaps.size());
 
   FaviconBitmap favicon_bitmap1 = favicon_bitmaps[0];
@@ -685,14 +684,14 @@
   EXPECT_EQ(kLargeSize, favicon_bitmap2.pixel_size);
 
   favicon_base::FaviconID out_favicon2 =
-      backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2,
-                                                         IconType::kFavicon);
+      backend_->favicon_db_->GetFaviconIDForFaviconURL(favicon_url2,
+                                                       IconType::kFavicon);
   EXPECT_FALSE(out_favicon2) << "Favicon not deleted";
 
   // The remaining URL should still reference the same favicon, even if its
   // ID has changed.
   std::vector<IconMapping> mappings;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(
       outrow1.url(), {IconType::kFavicon}, &mappings));
   EXPECT_EQ(1u, mappings.size());
   EXPECT_EQ(out_favicon1, mappings[0].icon_id);
@@ -726,10 +725,10 @@
   // Setup: Add favicon for |kPageURL|.
   std::vector<unsigned char> data;
   data.push_back('a');
-  favicon_base::FaviconID favicon = backend_->thumbnail_db_->AddFavicon(
+  favicon_base::FaviconID favicon = backend_->favicon_db_->AddFavicon(
       kFaviconURL, IconType::kFavicon, new base::RefCountedBytes(data),
       FaviconBitmapType::ON_VISIT, base::Time::Now(), kSmallSize);
-  backend_->thumbnail_db_->AddIconMapping(row.url(), favicon);
+  backend_->favicon_db_->AddIconMapping(row.url(), favicon);
 
   history_client_.AddBookmark(kPageURL);
 
@@ -741,7 +740,7 @@
   ASSERT_EQ(1U, visits.size());
 
   std::vector<IconMapping> icon_mappings;
-  ASSERT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+  ASSERT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(
       kPageURL, {IconType::kFavicon}, &icon_mappings));
   ASSERT_EQ(1u, icon_mappings.size());
 
@@ -754,7 +753,7 @@
   ASSERT_EQ(0, backend_->db_->GetRowForURL(kPageURL, nullptr));
 
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(
       kPageURL, {IconType::kFavicon}, &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
 }
@@ -812,12 +811,12 @@
 
   std::vector<unsigned char> data;
   data.push_back('1');
-  favicon_base::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
+  favicon_base::FaviconID favicon1 = backend_->favicon_db_->AddFavicon(
       favicon_url1, IconType::kFavicon, new base::RefCountedBytes(data),
       FaviconBitmapType::ON_VISIT, base::Time::Now(), gfx::Size());
 
   data[0] = '2';
-  favicon_base::FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(
+  favicon_base::FaviconID favicon2 = backend_->favicon_db_->AddFavicon(
       favicon_url2, IconType::kFavicon, new base::RefCountedBytes(data),
       FaviconBitmapType::ON_VISIT, base::Time::Now(), gfx::Size());
 
@@ -826,12 +825,12 @@
   row1.set_visit_count(2);
   row1.set_typed_count(1);
   row1.set_last_visit(base::Time::Now());
-  EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
+  EXPECT_TRUE(backend_->favicon_db_->AddIconMapping(row1.url(), favicon1));
 
   URLRow row2(GURL("http://news.google.com/"));
   row2.set_visit_count(1);
   row2.set_last_visit(base::Time::Now());
-  EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2));
+  EXPECT_TRUE(backend_->favicon_db_->AddIconMapping(row2.url(), favicon2));
 
   URLRows rows;
   rows.push_back(row2);  // Reversed order for the same reason as favicons.
@@ -852,7 +851,7 @@
   backend_->db_->GetVisitsForURL(row2_id, &visits);
   EXPECT_EQ(0U, visits.size());
   // The favicon should still be valid.
-  EXPECT_EQ(favicon2, backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+  EXPECT_EQ(favicon2, backend_->favicon_db_->GetFaviconIDForFaviconURL(
                           favicon_url2, IconType::kFavicon));
 
   // Unstar row2.
@@ -867,7 +866,7 @@
   // The URL should still not exist.
   EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), nullptr));
   // And the favicon should be deleted.
-  EXPECT_EQ(0, backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+  EXPECT_EQ(0, backend_->favicon_db_->GetFaviconIDForFaviconURL(
                    favicon_url2, IconType::kFavicon));
 
   // Unstar row 1.
@@ -888,7 +887,7 @@
   EXPECT_EQ(1U, visits.size());
 
   // The favicon should still be valid.
-  EXPECT_EQ(favicon1, backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+  EXPECT_EQ(favicon1, backend_->favicon_db_->GetFaviconIDForFaviconURL(
                           favicon_url1, IconType::kFavicon));
 }
 
@@ -1150,14 +1149,14 @@
   GURL favicon_url1("http://www.google.com/favicon.ico");
   std::vector<unsigned char> data;
   data.push_back('1');
-  favicon_base::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
+  favicon_base::FaviconID favicon1 = backend_->favicon_db_->AddFavicon(
       favicon_url1, IconType::kFavicon,
       base::RefCountedBytes::TakeVector(&data), FaviconBitmapType::ON_VISIT,
       base::Time::Now(), gfx::Size());
   URLRow row1(GURL("http://www.google.com/"));
   row1.set_visit_count(1);
   row1.set_last_visit(base::Time::Now());
-  EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
+  EXPECT_TRUE(backend_->favicon_db_->AddIconMapping(row1.url(), favicon1));
 
   URLRow row2(GURL("http://news.google.com/"));
   row2.set_visit_count(1);
@@ -1187,14 +1186,14 @@
   EXPECT_NE(0, backend_->db_->GetRowForURL(row2.url(), &url_row2));
 
   std::vector<IconMapping> mappings;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(
       row1.url(), {IconType::kFavicon}, &mappings));
   EXPECT_EQ(1u, mappings.size());
   EXPECT_EQ(favicon1, mappings[0].icon_id);
   EXPECT_EQ(favicon_url1, mappings[0].icon_url);
 
   mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(
       row2.url(), {IconType::kFavicon}, &mappings));
   EXPECT_EQ(1u, mappings.size());
   EXPECT_EQ(favicon.favicon_url, mappings[0].icon_url);
@@ -1837,7 +1836,7 @@
   backend_->SetFavicons({url}, IconType::kFavicon, icon_url, bitmaps);
 
   std::vector<IconMapping> icon_mappings;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(
       url, {IconType::kFavicon}, &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   IconMappingID mapping_id = icon_mappings[0].mapping_id;
@@ -1845,7 +1844,7 @@
   backend_->SetFavicons({url}, IconType::kFavicon, icon_url, bitmaps);
 
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(
       url, {IconType::kFavicon}, &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
 
@@ -1893,16 +1892,16 @@
 
   scoped_refptr<base::RefCountedMemory> bitmap_data_out;
   gfx::Size pixel_size_out;
-  EXPECT_FALSE(backend_->thumbnail_db_->GetFaviconBitmap(
+  EXPECT_FALSE(backend_->favicon_db_->GetFaviconBitmap(
       small_bitmap_id, nullptr, nullptr, &bitmap_data_out, &pixel_size_out));
-  EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmap(
+  EXPECT_TRUE(backend_->favicon_db_->GetFaviconBitmap(
       large_bitmap_id, nullptr, nullptr, &bitmap_data_out, &pixel_size_out));
   EXPECT_TRUE(BitmapColorEqual(SK_ColorWHITE, bitmap_data_out));
   EXPECT_EQ(kLargeSize, pixel_size_out);
 
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
 }
@@ -1918,8 +1917,8 @@
   backend_->SetFavicons({page_url}, IconType::kFavicon, icon_url, bitmaps);
 
   favicon_base::FaviconID original_favicon_id =
-      backend_->thumbnail_db_->GetFaviconIDForFaviconURL(icon_url,
-                                                         IconType::kFavicon);
+      backend_->favicon_db_->GetFaviconIDForFaviconURL(icon_url,
+                                                       IconType::kFavicon);
   EXPECT_NE(0, original_favicon_id);
   FaviconBitmap original_favicon_bitmap;
   EXPECT_TRUE(
@@ -1933,8 +1932,8 @@
   backend_->SetFavicons({page_url}, IconType::kFavicon, icon_url, bitmaps);
 
   favicon_base::FaviconID updated_favicon_id =
-      backend_->thumbnail_db_->GetFaviconIDForFaviconURL(icon_url,
-                                                         IconType::kFavicon);
+      backend_->favicon_db_->GetFaviconIDForFaviconURL(icon_url,
+                                                       IconType::kFavicon);
   EXPECT_NE(0, updated_favicon_id);
   FaviconBitmap updated_favicon_bitmap;
   EXPECT_TRUE(
@@ -1947,7 +1946,7 @@
   bitmaps[0] = CreateBitmap(SK_ColorWHITE, kSmallEdgeSize);
   backend_->SetFavicons({page_url}, IconType::kFavicon, icon_url, bitmaps);
 
-  updated_favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+  updated_favicon_id = backend_->favicon_db_->GetFaviconIDForFaviconURL(
       icon_url, IconType::kFavicon);
   EXPECT_NE(0, updated_favicon_id);
   EXPECT_TRUE(
@@ -1982,15 +1981,15 @@
 
   // Check that the same FaviconID is mapped to both page URLs.
   std::vector<IconMapping> icon_mappings;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url1, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url1,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
   EXPECT_NE(0, favicon_id);
 
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url2, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url2,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
 
@@ -2001,28 +2000,28 @@
 
   // |page_url1| should map to a new FaviconID and have valid bitmap data.
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url1, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url1,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(icon_url_new, icon_mappings[0].icon_url);
   EXPECT_NE(favicon_id, icon_mappings[0].icon_id);
 
   std::vector<FaviconBitmap> favicon_bitmaps;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
-      icon_mappings[0].icon_id, &favicon_bitmaps));
+  EXPECT_TRUE(backend_->favicon_db_->GetFaviconBitmaps(icon_mappings[0].icon_id,
+                                                       &favicon_bitmaps));
   EXPECT_EQ(1u, favicon_bitmaps.size());
 
   // |page_url2| should still map to the same FaviconID and have valid bitmap
   // data.
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url2, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url2,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
 
   favicon_bitmaps.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(favicon_id,
-                                                         &favicon_bitmaps));
+  EXPECT_TRUE(
+      backend_->favicon_db_->GetFaviconBitmaps(favicon_id, &favicon_bitmaps));
   EXPECT_EQ(2u, favicon_bitmaps.size());
 }
 
@@ -2040,15 +2039,15 @@
                         bitmaps);
 
   std::vector<IconMapping> icon_mappings;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url1, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url1,
+                                                               &icon_mappings));
   ASSERT_EQ(1u, icon_mappings.size());
   favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
   EXPECT_NE(0, favicon_id);
 
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url2, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url2,
+                                                               &icon_mappings));
   ASSERT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
 }
@@ -2091,8 +2090,8 @@
                                             icon_url, bitmaps));
 
   favicon_base::FaviconID favicon_id =
-      backend_->thumbnail_db_->GetFaviconIDForFaviconURL(icon_url,
-                                                         IconType::kFavicon);
+      backend_->favicon_db_->GetFaviconIDForFaviconURL(icon_url,
+                                                       IconType::kFavicon);
   EXPECT_NE(0, favicon_id);
 
   FaviconBitmap favicon_bitmap;
@@ -2122,15 +2121,15 @@
   // Add bitmap to the database.
   backend_->SetFavicons({page_url}, IconType::kFavicon, icon_url1, bitmaps);
   favicon_base::FaviconID original_favicon_id =
-      backend_->thumbnail_db_->GetFaviconIDForFaviconURL(icon_url1,
-                                                         IconType::kFavicon);
+      backend_->favicon_db_->GetFaviconIDForFaviconURL(icon_url1,
+                                                       IconType::kFavicon);
   ASSERT_NE(0, original_favicon_id);
 
   // Call SetOnDemandFavicons() with a different icon URL and bitmap data.
   bitmaps[0] = CreateBitmap(SK_ColorWHITE, kSmallEdgeSize);
   EXPECT_FALSE(backend_->SetOnDemandFavicons(page_url, IconType::kFavicon,
                                              icon_url2, bitmaps));
-  EXPECT_EQ(0, backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+  EXPECT_EQ(0, backend_->favicon_db_->GetFaviconIDForFaviconURL(
                    icon_url2, IconType::kFavicon));
 
   FaviconBitmap favicon_bitmap;
@@ -2160,8 +2159,8 @@
   // Add bitmap to the database.
   backend_->SetFavicons({old_page_url}, IconType::kFavicon, icon_url, bitmaps);
   favicon_base::FaviconID original_favicon_id =
-      backend_->thumbnail_db_->GetFaviconIDForFaviconURL(icon_url,
-                                                         IconType::kFavicon);
+      backend_->favicon_db_->GetFaviconIDForFaviconURL(icon_url,
+                                                       IconType::kFavicon);
   ASSERT_NE(0, original_favicon_id);
 
   // Call SetOnDemandFavicons() with a different bitmap.
@@ -2170,7 +2169,7 @@
                                              icon_url, bitmaps));
 
   EXPECT_EQ(original_favicon_id,
-            backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+            backend_->favicon_db_->GetFaviconIDForFaviconURL(
                 icon_url, IconType::kFavicon));
 
   FaviconBitmap favicon_bitmap;
@@ -2205,8 +2204,8 @@
   // |page_url| should now be mapped to |icon_url| and the favicon bitmap should
   // be expired.
   std::vector<IconMapping> icon_mappings;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
 
@@ -2224,8 +2223,8 @@
   // |page_url| should still have a single favicon bitmap. The bitmap data
   // should be updated.
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
 
@@ -2247,8 +2246,8 @@
 
   // Test initial state.
   std::vector<IconMapping> icon_mappings;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
 
@@ -2269,8 +2268,8 @@
   // All the data should stay the same and no notifications should have been
   // sent.
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
 
@@ -2288,8 +2287,8 @@
 
   // The small favicon bitmap at |icon_url1| should be overwritten.
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
 
@@ -2308,8 +2307,8 @@
   // A new favicon bitmap should be created and the preexisting favicon bitmap
   // ('b') should be expired.
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
 
@@ -2333,8 +2332,8 @@
   // The existing favicon bitmaps should be copied over to the newly created
   // favicon at |icon_url2|. |page_url| should solely be mapped to |icon_url2|.
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(icon_url2, icon_mappings[0].icon_url);
 
@@ -2365,8 +2364,8 @@
 
   // Test initial state.
   std::vector<IconMapping> icon_mappings;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url1, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url1,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
 
@@ -2386,8 +2385,8 @@
                          kSmallSize);
 
   favicon_base::FaviconID favicon_id =
-      backend_->thumbnail_db_->GetFaviconIDForFaviconURL(icon_url,
-                                                         IconType::kFavicon);
+      backend_->favicon_db_->GetFaviconIDForFaviconURL(icon_url,
+                                                       IconType::kFavicon);
   EXPECT_NE(0, favicon_id);
 
   EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
@@ -2403,7 +2402,7 @@
   backend_->MergeFavicon(page_url3, icon_url, IconType::kFavicon, bitmap_data,
                          kSmallSize);
 
-  favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
+  favicon_id = backend_->favicon_db_->GetFaviconIDForFaviconURL(
       icon_url, IconType::kFavicon);
   EXPECT_NE(0, favicon_id);
 
@@ -2414,20 +2413,20 @@
 
   // |icon_url| should be mapped to all three page URLs.
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url1, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url1,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
 
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url2, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url2,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
 
   icon_mappings.clear();
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url3, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url3,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
 }
@@ -2457,12 +2456,12 @@
   // There should be a single favicon mapped to |page_url| with exactly
   // kMaxFaviconBitmapsPerIconURL favicon bitmaps.
   std::vector<IconMapping> icon_mappings;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url, &icon_mappings));
+  EXPECT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url,
+                                                               &icon_mappings));
   EXPECT_EQ(1u, icon_mappings.size());
   std::vector<FaviconBitmap> favicon_bitmaps;
-  EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
-      icon_mappings[0].icon_id, &favicon_bitmaps));
+  EXPECT_TRUE(backend_->favicon_db_->GetFaviconBitmaps(icon_mappings[0].icon_id,
+                                                       &favicon_bitmaps));
   EXPECT_EQ(kMaxFaviconBitmapsPerIconURL, favicon_bitmaps.size());
 }
 
@@ -2793,17 +2792,17 @@
 
   // Find the ID of the add favicon bitmap.
   std::vector<IconMapping> icon_mappings;
-  ASSERT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url, &icon_mappings));
+  ASSERT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url,
+                                                               &icon_mappings));
   ASSERT_EQ(1u, icon_mappings.size());
   std::vector<FaviconBitmap> favicon_bitmaps;
-  ASSERT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
-      icon_mappings[0].icon_id, &favicon_bitmaps));
+  ASSERT_TRUE(backend_->favicon_db_->GetFaviconBitmaps(icon_mappings[0].icon_id,
+                                                       &favicon_bitmaps));
 
   // Change the last updated time of the just added favicon bitmap.
   const base::Time kLastUpdateTime =
       base::Time::Now() - base::TimeDelta::FromDays(314);
-  backend_->thumbnail_db_->SetFaviconBitmapLastUpdateTime(
+  backend_->favicon_db_->SetFaviconBitmapLastUpdateTime(
       favicon_bitmaps[0].bitmap_id, kLastUpdateTime);
 
   // Call MergeFavicon() with identical data.
@@ -2812,12 +2811,12 @@
 
   // Check that the "last updated" time did not change.
   icon_mappings.clear();
-  ASSERT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
-      page_url, &icon_mappings));
+  ASSERT_TRUE(backend_->favicon_db_->GetIconMappingsForPageURL(page_url,
+                                                               &icon_mappings));
   ASSERT_EQ(1u, icon_mappings.size());
   favicon_bitmaps.clear();
-  ASSERT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
-      icon_mappings[0].icon_id, &favicon_bitmaps));
+  ASSERT_TRUE(backend_->favicon_db_->GetFaviconBitmaps(icon_mappings[0].icon_id,
+                                                       &favicon_bitmaps));
   EXPECT_EQ(kLastUpdateTime, favicon_bitmaps[0].last_updated);
 }
 
@@ -2973,9 +2972,9 @@
   const GURL icon_url("http://www.google.com/icon1");
 
   favicon_base::FaviconID icon_id =
-      backend_->thumbnail_db_->AddFavicon(icon_url, IconType::kFavicon);
+      backend_->favicon_db_->AddFavicon(icon_url, IconType::kFavicon);
   EXPECT_NE(0, icon_id);
-  EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
+  EXPECT_NE(0, backend_->favicon_db_->AddIconMapping(page_url, icon_id));
 
   std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
   EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url, {IconType::kFavicon},
@@ -3216,11 +3215,11 @@
   scoped_refptr<base::RefCountedBytes> bitmap_data(
       base::RefCountedBytes::TakeVector(&data));
   base::Time last_updated = base::Time::FromTimeT(0);
-  favicon_base::FaviconID icon_id = backend_->thumbnail_db_->AddFavicon(
+  favicon_base::FaviconID icon_id = backend_->favicon_db_->AddFavicon(
       icon_url, IconType::kFavicon, bitmap_data, FaviconBitmapType::ON_VISIT,
       last_updated, kSmallSize);
   EXPECT_NE(0, icon_id);
-  EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
+  EXPECT_NE(0, backend_->favicon_db_->AddIconMapping(page_url, icon_id));
 
   std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
   EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url, {IconType::kFavicon},
@@ -3232,10 +3231,10 @@
 }
 
 // Check that UpdateFaviconMappingsAndFetch() call back to the UI when there is
-// no valid thumbnail database.
+// no valid favicon database.
 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoDB) {
-  // Make the thumbnail database invalid.
-  backend_->thumbnail_db_.reset();
+  // Make the favicon database invalid.
+  backend_->favicon_db_.reset();
 
   std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results =
       backend_->UpdateFaviconMappingsAndFetch(
diff --git a/components/history/core/browser/history_service.cc b/components/history/core/browser/history_service.cc
index ac7058c..2b51be22 100644
--- a/components/history/core/browser/history_service.cc
+++ b/components/history/core/browser/history_service.cc
@@ -12,9 +12,8 @@
 // HistoryService <----------------> HistoryBackend
 //                                   -> HistoryDatabase
 //                                      -> SQLite connection to History
-//                                   -> ThumbnailDatabase
-//                                      -> SQLite connection to Thumbnails
-//                                         (and favicons)
+//                                   -> FaviconDatabase
+//                                      -> SQLite connection to favicons
 
 #include "components/history/core/browser/history_service.h"
 
diff --git a/components/history/core/browser/top_sites_database.cc b/components/history/core/browser/top_sites_database.cc
index 4af4531..69acd49 100644
--- a/components/history/core/browser/top_sites_database.cc
+++ b/components/history/core/browser/top_sites_database.cc
@@ -565,7 +565,7 @@
 
 sql::Database* TopSitesDatabase::CreateDB(const base::FilePath& db_name) {
   std::unique_ptr<sql::Database> db(new sql::Database());
-  // Settings copied from ThumbnailDatabase.
+  // Settings copied from FaviconDatabase.
   db->set_histogram_tag("TopSites");
   db->set_error_callback(
       base::BindRepeating(&DatabaseErrorCallback, db.get(), db_name));
diff --git a/components/history/core/test/history_client_fake_bookmarks.cc b/components/history/core/test/history_client_fake_bookmarks.cc
index 003b27db..0c05a99f 100644
--- a/components/history/core/test/history_client_fake_bookmarks.cc
+++ b/components/history/core/test/history_client_fake_bookmarks.cc
@@ -86,7 +86,7 @@
 #if defined(OS_ANDROID)
   void OnHistoryBackendInitialized(HistoryBackend* history_backend,
                                    HistoryDatabase* history_database,
-                                   ThumbnailDatabase* thumbnail_database,
+                                   FaviconDatabase* favicon_database,
                                    const base::FilePath& history_dir) override;
   void OnHistoryBackendDestroyed(HistoryBackend* history_backend,
                                  const base::FilePath& history_dir) override;
@@ -122,9 +122,8 @@
 void HistoryBackendClientFakeBookmarks::OnHistoryBackendInitialized(
     HistoryBackend* history_backend,
     HistoryDatabase* history_database,
-    ThumbnailDatabase* thumbnail_database,
-    const base::FilePath& history_dir) {
-}
+    FaviconDatabase* favicon_database,
+    const base::FilePath& history_dir) {}
 
 void HistoryBackendClientFakeBookmarks::OnHistoryBackendDestroyed(
     HistoryBackend* history_backend,
diff --git a/components/javascript_dialogs/app_modal_dialog_controller.cc b/components/javascript_dialogs/app_modal_dialog_controller.cc
index 29008c0..025464d 100644
--- a/components/javascript_dialogs/app_modal_dialog_controller.cc
+++ b/components/javascript_dialogs/app_modal_dialog_controller.cc
@@ -78,7 +78,6 @@
     bool is_reload,
     content::JavaScriptDialogManager::DialogClosedCallback callback)
     : title_(title),
-      completed_(false),
       valid_(true),
       view_(nullptr),
       web_contents_(web_contents),
@@ -114,9 +113,13 @@
 }
 
 void AppModalDialogController::CompleteDialog() {
-  if (!completed_) {
-    completed_ = true;
+  // If |view_| is non-null, then |this| is the active dialog and the next one
+  // should be shown. Otherwise, |this| was never shown.
+  if (view_) {
+    view_ = nullptr;
     AppModalDialogQueue::GetInstance()->ShowNextDialog();
+  } else {
+    DCHECK(!valid_);
   }
 }
 
diff --git a/components/javascript_dialogs/app_modal_dialog_controller.h b/components/javascript_dialogs/app_modal_dialog_controller.h
index 03c75e07..0450d77 100644
--- a/components/javascript_dialogs/app_modal_dialog_controller.h
+++ b/components/javascript_dialogs/app_modal_dialog_controller.h
@@ -107,14 +107,12 @@
   // The title of the dialog.
   const base::string16 title_;
 
-  // // True if CompleteDialog was called.
-  bool completed_;
-
   // False if the dialog should no longer be shown, e.g. because the underlying
   // tab navigated away while the dialog was queued.
   bool valid_;
 
-  // // The toolkit-specific implementation of the app modal dialog box.
+  // The toolkit-specific implementation of the app modal dialog box. When
+  // non-null, |view_| owns |this|.
   AppModalDialogView* view_;
 
   // The WebContents that opened this dialog.
diff --git a/components/javascript_dialogs/app_modal_dialog_manager.cc b/components/javascript_dialogs/app_modal_dialog_manager.cc
index 07463ba..16967487 100644
--- a/components/javascript_dialogs/app_modal_dialog_manager.cc
+++ b/components/javascript_dialogs/app_modal_dialog_manager.cc
@@ -278,15 +278,11 @@
 void AppModalDialogManager::CancelDialogs(content::WebContents* web_contents,
                                           bool reset_state) {
   AppModalDialogQueue* queue = AppModalDialogQueue::GetInstance();
-  AppModalDialogController* active_dialog = queue->active_dialog();
   for (auto* dialog : *queue) {
-    // Invalidating the active dialog might trigger showing a not-yet
-    // invalidated dialog, so invalidate the active dialog last.
-    if (dialog == active_dialog)
-      continue;
     if (dialog->web_contents() == web_contents)
       dialog->Invalidate();
   }
+  AppModalDialogController* active_dialog = queue->active_dialog();
   if (active_dialog && active_dialog->web_contents() == web_contents)
     active_dialog->Invalidate();
 
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc
index 6ee9051..b60f9021 100644
--- a/components/omnibox/browser/autocomplete_match.cc
+++ b/components/omnibox/browser/autocomplete_match.cc
@@ -1186,8 +1186,13 @@
     return true;
   }
 
+  const bool can_autocomplete_titles =
+      OmniboxFieldTrial::RichAutocompletionAutocompleteTitles() &&
+      input.text().size() >=
+          OmniboxFieldTrial::RichAutocompletionAutocompleteTitlesMinChar();
+
   // Try matching the prefix of |secondary_text|.
-  if (OmniboxFieldTrial::RichAutocompletionAutocompleteTitles() &&
+  if (can_autocomplete_titles &&
       base::StartsWith(secondary_text_lower, input_text_lower,
                        base::CompareCase::SENSITIVE)) {
     fill_into_edit = secondary_text;
@@ -1198,7 +1203,10 @@
     return true;
   }
 
-  if (!OmniboxFieldTrial::RichAutocompletionAutocompleteNonPrefix())
+  // Check if non-prefix autocompletion is possible.
+  if (!OmniboxFieldTrial::RichAutocompletionAutocompleteNonPrefix() ||
+      input.text().size() <
+          OmniboxFieldTrial::RichAutocompletionAutocompleteNonPrefixMinChar())
     return false;
 
   // Try matching a non-prefix the |primary_text|.
@@ -1215,8 +1223,7 @@
 
   // Try matching a non-prefix the |secondary_text|.
   size_t secondary_find_index = secondary_text_lower.find(input_text_lower);
-  if (OmniboxFieldTrial::RichAutocompletionAutocompleteTitles() &&
-      secondary_find_index != base::string16::npos) {
+  if (can_autocomplete_titles && secondary_find_index != base::string16::npos) {
     fill_into_edit = secondary_text;
     fill_into_edit_additional_text = primary_text;
     inline_autocompletion =
diff --git a/components/omnibox/browser/autocomplete_match_unittest.cc b/components/omnibox/browser/autocomplete_match_unittest.cc
index 9cd09ca..3277eb4 100644
--- a/components/omnibox/browser/autocomplete_match_unittest.cc
+++ b/components/omnibox/browser/autocomplete_match_unittest.cc
@@ -570,7 +570,25 @@
          "x_mid_x_secondary", false);
   }
 
+  {
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndEnableFeatureWithParameters(
+        omnibox::kRichAutocompletion,
+        {
+            {"RichAutocompletionAutocompleteTitles", "true"},
+            {"RichAutocompletionTwoLineOmnibox", "true"},
+            {"RichAutocompletionShowTitles", "true"},
+            {"RichAutocompletionAutocompleteNonPrefix", "true"},
+            {"RichAutocompletionAutocompleteTitlesMinChar", "2"},
+            {"RichAutocompletionAutocompleteNonPrefixMinChar", "2"},
+        });
+
+    // Don't autocomplete title and non-prefix if input is less than limits.
+    test(6, "x", false, "y_mid_x_primary", "x_mid_x_secondary", false, "", "",
+         "x_mid_x_secondary", false);
+  }
+
   // Don't autocomplete if IsRichAutocompletionEnabled is disabled
-  test(6, "x", false, "x_mid_x_primary", "x_mid_x_secondary", false, "", "", "",
+  test(7, "x", false, "x_mid_x_primary", "x_mid_x_secondary", false, "", "", "",
        false);
 }
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 2f8a33a..ec0e49e5 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -741,6 +741,12 @@
       false);
 }
 
+size_t OmniboxFieldTrial::RichAutocompletionAutocompleteTitlesMinChar() {
+  return base::GetFieldTrialParamByFeatureAsInt(
+      omnibox::kRichAutocompletion,
+      kRichAutocompletionAutocompleteTitlesMinCharParam, 0);
+}
+
 bool OmniboxFieldTrial::RichAutocompletionTwoLineOmnibox() {
   return base::GetFieldTrialParamByFeatureAsBool(
       omnibox::kRichAutocompletion, kRichAutocompletionTwoLineOmniboxParam,
@@ -754,8 +760,14 @@
 
 bool OmniboxFieldTrial::RichAutocompletionAutocompleteNonPrefix() {
   return base::GetFieldTrialParamByFeatureAsBool(
-      omnibox::kRichAutocompletion, kRichAutocompletionAutocompleteNonPrefix,
-      false);
+      omnibox::kRichAutocompletion,
+      kRichAutocompletionAutocompleteNonPrefixParam, false);
+}
+
+size_t OmniboxFieldTrial::RichAutocompletionAutocompleteNonPrefixMinChar() {
+  return base::GetFieldTrialParamByFeatureAsInt(
+      omnibox::kRichAutocompletion,
+      kRichAutocompletionAutocompleteNonPrefixMinCharParam, 0);
 }
 
 bool OmniboxFieldTrial::IsOnDeviceHeadSuggestEnabledForIncognito() {
@@ -932,12 +944,18 @@
 
 const char OmniboxFieldTrial::kRichAutocompletionAutocompleteTitlesParam[] =
     "RichAutocompletionAutocompleteTitles";
+const char
+    OmniboxFieldTrial::kRichAutocompletionAutocompleteTitlesMinCharParam[] =
+        "RichAutocompletionAutocompleteTitlesMinChar";
 const char OmniboxFieldTrial::kRichAutocompletionTwoLineOmniboxParam[] =
     "RichAutocompletionTwoLineOmnibox";
 const char OmniboxFieldTrial::kRichAutocompletionShowTitlesParam[] =
     "RichAutocompletionShowTitles";
-const char OmniboxFieldTrial::kRichAutocompletionAutocompleteNonPrefix[] =
+const char OmniboxFieldTrial::kRichAutocompletionAutocompleteNonPrefixParam[] =
     "RichAutocompletionAutocompleteNonPrefix";
+const char
+    OmniboxFieldTrial::kRichAutocompletionAutocompleteNonPrefixMinCharParam[] =
+        "RichAutocompletionAutocompleteNonPrefixMinChar";
 
 const char OmniboxFieldTrial::kImageSearchSuggestionThumbnail[] =
     "ImageSearchSuggestionThumbnail";
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 219b61f1..eaa4d185 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -411,9 +411,11 @@
 // Rich autocompletion.
 bool IsRichAutocompletionEnabled();
 bool RichAutocompletionAutocompleteTitles();
+size_t RichAutocompletionAutocompleteTitlesMinChar();
 bool RichAutocompletionTwoLineOmnibox();
 bool RichAutocompletionShowTitles();
 bool RichAutocompletionAutocompleteNonPrefix();
+size_t RichAutocompletionAutocompleteNonPrefixMinChar();
 
 // On Device Head Suggestions feature and its helper functions.
 bool IsOnDeviceHeadSuggestEnabledForIncognito();
@@ -531,9 +533,11 @@
 
 // Parameter names used for rich autocompletion variations.
 extern const char kRichAutocompletionAutocompleteTitlesParam[];
+extern const char kRichAutocompletionAutocompleteTitlesMinCharParam[];
 extern const char kRichAutocompletionTwoLineOmniboxParam[];
 extern const char kRichAutocompletionShowTitlesParam[];
-extern const char kRichAutocompletionAutocompleteNonPrefix[];
+extern const char kRichAutocompletionAutocompleteNonPrefixParam[];
+extern const char kRichAutocompletionAutocompleteNonPrefixMinCharParam[];
 
 // Parameter names used by image search experiment that shows thumbnail in front
 // of the Omnibox clipboard image search suggestion.
diff --git a/components/omnibox/browser/omnibox_view_unittest.cc b/components/omnibox/browser/omnibox_view_unittest.cc
index fa1bad6..7e7ab4b 100644
--- a/components/omnibox/browser/omnibox_view_unittest.cc
+++ b/components/omnibox/browser/omnibox_view_unittest.cc
@@ -272,7 +272,8 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeatureWithParameters(
       omnibox::kRichAutocompletion,
-      {{OmniboxFieldTrial::kRichAutocompletionAutocompleteNonPrefix, "true"}});
+      {{OmniboxFieldTrial::kRichAutocompletionAutocompleteNonPrefixParam,
+        "true"}});
 
   // Cases with single selection
 
diff --git a/components/pdf/renderer/pdf_accessibility_tree.cc b/components/pdf/renderer/pdf_accessibility_tree.cc
index ce56071..5aa99e4 100644
--- a/components/pdf/renderer/pdf_accessibility_tree.cc
+++ b/components/pdf/renderer/pdf_accessibility_tree.cc
@@ -377,6 +377,7 @@
         highlights_(page_objects.highlights),
         text_fields_(page_objects.form_fields.text_fields),
         buttons_(page_objects.form_fields.buttons),
+        choice_fields_(page_objects.form_fields.choice_fields),
         page_bounds_(page_bounds),
         page_index_(page_index),
         page_node_(page_node),
@@ -408,6 +409,7 @@
     uint32_t current_highlight_index = 0;
     uint32_t current_text_field_index = 0;
     uint32_t current_button_index = 0;
+    uint32_t current_choice_field_index = 0;
     LineHelper line_helper(text_runs_);
     bool pdf_forms_enabled =
         base::FeatureList::IsEnabled(chrome_pdf::features::kAccessiblePDFForm);
@@ -461,6 +463,13 @@
         AddButtonToParaNode(buttons_[current_button_index++], para_node,
                             &text_run_index);
         continue;
+      } else if (IsObjectInTextRun(choice_fields_, current_choice_field_index,
+                                   text_run_index) &&
+                 pdf_forms_enabled) {
+        FinishStaticNode(&static_text_node, &static_text);
+        AddChoiceFieldToParaNode(choice_fields_[current_choice_field_index++],
+                                 para_node, &text_run_index);
+        continue;
       } else {
         PP_PdfPageCharacterIndex page_char_index = {
             page_index_, text_run_start_indices_[text_run_index]};
@@ -529,9 +538,12 @@
             base::make_span(text_fields_).subspan(current_text_field_index);
     base::span<const ppapi::PdfAccessibilityButtonInfo> remaining_buttons =
         base::make_span(buttons_).subspan(current_button_index);
+    base::span<const ppapi::PdfAccessibilityChoiceFieldInfo>
+        remaining_choice_fields =
+            base::make_span(choice_fields_).subspan(current_text_field_index);
     AddRemainingAnnotations(remaining_links, remaining_images,
                             remaining_text_fields, remaining_buttons,
-                            para_node);
+                            remaining_choice_fields, para_node);
   }
 
  private:
@@ -752,6 +764,120 @@
     return button_node;
   }
 
+  ui::AXNodeData* CreateListboxOptionNode(
+      const ppapi::PdfAccessibilityChoiceFieldOptionInfo& choice_field_option,
+      ax::mojom::Restriction restriction) {
+    ui::AXNodeData* listbox_option_node =
+        CreateNode(ax::mojom::Role::kListBoxOption, restriction,
+                   render_accessibility_, nodes_);
+
+    listbox_option_node->AddStringAttribute(ax::mojom::StringAttribute::kName,
+                                            choice_field_option.name);
+    listbox_option_node->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected,
+                                          choice_field_option.is_selected);
+    listbox_option_node->AddState(ax::mojom::State::kFocusable);
+    return listbox_option_node;
+  }
+
+  ui::AXNodeData* CreateListboxNode(
+      const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field,
+      ui::AXNodeData* control_node) {
+    ax::mojom::Restriction restriction = choice_field.is_read_only
+                                             ? ax::mojom::Restriction::kReadOnly
+                                             : ax::mojom::Restriction::kNone;
+    ui::AXNodeData* listbox_node = CreateNode(
+        ax::mojom::Role::kListBox, restriction, render_accessibility_, nodes_);
+
+    if (choice_field.type != PP_PRIVATECHOICEFIELD_COMBOBOX) {
+      listbox_node->AddStringAttribute(ax::mojom::StringAttribute::kName,
+                                       choice_field.name);
+    }
+
+    ui::AXNodeData* first_selected_option = nullptr;
+    for (const ppapi::PdfAccessibilityChoiceFieldOptionInfo& option :
+         choice_field.options) {
+      ui::AXNodeData* listbox_option_node =
+          CreateListboxOptionNode(option, restriction);
+      if (!first_selected_option && listbox_option_node->GetBoolAttribute(
+                                        ax::mojom::BoolAttribute::kSelected)) {
+        first_selected_option = listbox_option_node;
+      }
+      // TODO(bug 1030242): add |listbox_option_node| specific bounds here.
+      listbox_option_node->relative_bounds.bounds =
+          PpFloatRectToGfxRectF(choice_field.bounds);
+      listbox_node->child_ids.push_back(listbox_option_node->id);
+    }
+
+    if (control_node && first_selected_option) {
+      control_node->AddIntAttribute(
+          ax::mojom::IntAttribute::kActivedescendantId,
+          first_selected_option->id);
+    }
+
+    if (choice_field.is_multi_select)
+      listbox_node->AddState(ax::mojom::State::kMultiselectable);
+    listbox_node->AddState(ax::mojom::State::kFocusable);
+    listbox_node->relative_bounds.bounds =
+        PpFloatRectToGfxRectF(choice_field.bounds);
+    return listbox_node;
+  }
+
+  ui::AXNodeData* CreateComboboxInputNode(
+      const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field,
+      ax::mojom::Restriction restriction) {
+    ax::mojom::Role input_role = choice_field.has_editable_text_box
+                                     ? ax::mojom::Role::kTextFieldWithComboBox
+                                     : ax::mojom::Role::kComboBoxMenuButton;
+    ui::AXNodeData* combobox_input_node =
+        CreateNode(input_role, restriction, render_accessibility_, nodes_);
+    combobox_input_node->AddStringAttribute(ax::mojom::StringAttribute::kName,
+                                            choice_field.name);
+    for (const ppapi::PdfAccessibilityChoiceFieldOptionInfo& option :
+         choice_field.options) {
+      if (option.is_selected) {
+        combobox_input_node->AddStringAttribute(
+            ax::mojom::StringAttribute::kValue, option.name);
+        break;
+      }
+    }
+
+    combobox_input_node->AddState(ax::mojom::State::kFocusable);
+    combobox_input_node->relative_bounds.bounds =
+        PpFloatRectToGfxRectF(choice_field.bounds);
+    return combobox_input_node;
+  }
+
+  ui::AXNodeData* CreateComboboxNode(
+      const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field) {
+    ax::mojom::Restriction restriction = choice_field.is_read_only
+                                             ? ax::mojom::Restriction::kReadOnly
+                                             : ax::mojom::Restriction::kNone;
+    ui::AXNodeData* combobox_node =
+        CreateNode(ax::mojom::Role::kComboBoxGrouping, restriction,
+                   render_accessibility_, nodes_);
+    ui::AXNodeData* input_element =
+        CreateComboboxInputNode(choice_field, restriction);
+    ui::AXNodeData* list_element =
+        CreateListboxNode(choice_field, input_element);
+    input_element->AddIntListAttribute(
+        ax::mojom::IntListAttribute::kControlsIds,
+        std::vector<int32_t>{list_element->id});
+    combobox_node->child_ids.push_back(input_element->id);
+    combobox_node->child_ids.push_back(list_element->id);
+    combobox_node->AddState(ax::mojom::State::kFocusable);
+    combobox_node->relative_bounds.bounds =
+        PpFloatRectToGfxRectF(choice_field.bounds);
+    return combobox_node;
+  }
+
+  ui::AXNodeData* CreateChoiceFieldNode(
+      const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field) {
+    if (choice_field.type == PP_PRIVATECHOICEFIELD_LISTBOX)
+      return CreateListboxNode(choice_field, nullptr);
+
+    return CreateComboboxNode(choice_field);
+  }
+
   void AddTextToAXNode(uint32_t start_text_run_index,
                        uint32_t end_text_run_index,
                        ui::AXNodeData* ax_node,
@@ -919,16 +1045,28 @@
     --(*text_run_index);
   }
 
+  void AddChoiceFieldToParaNode(
+      const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field,
+      ui::AXNodeData* para_node,
+      size_t* text_run_index) {
+    // If the |text_run_index| is less than or equal to the choice_field's text
+    // run index, then push the choice_field ahead of the current text run.
+    ui::AXNodeData* choice_field_node = CreateChoiceFieldNode(choice_field);
+    para_node->child_ids.push_back(choice_field_node->id);
+    --(*text_run_index);
+  }
+
   void AddRemainingAnnotations(
       base::span<const ppapi::PdfAccessibilityLinkInfo> links,
       base::span<const ppapi::PdfAccessibilityImageInfo> images,
       base::span<const ppapi::PdfAccessibilityTextFieldInfo> text_fields,
       base::span<const ppapi::PdfAccessibilityButtonInfo> buttons,
+      base::span<const ppapi::PdfAccessibilityChoiceFieldInfo> choice_fields,
       ui::AXNodeData* para_node) {
     // If we don't have additional links, images or form fields to insert in the
     // tree, then return.
     if (links.empty() && images.empty() && text_fields.empty() &&
-        buttons.empty()) {
+        buttons.empty() && choice_fields.empty()) {
       return;
     }
 
@@ -966,6 +1104,14 @@
         ui::AXNodeData* button_node = CreateButtonNode(button);
         para_node->child_ids.push_back(button_node->id);
       }
+
+      // Push all the choice fields not anchored to any text run to the last
+      // paragraph.
+      for (const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field :
+           choice_fields) {
+        ui::AXNodeData* choice_field_node = CreateChoiceFieldNode(choice_field);
+        para_node->child_ids.push_back(choice_field_node->id);
+      }
     }
   }
 
@@ -977,6 +1123,7 @@
   const std::vector<ppapi::PdfAccessibilityHighlightInfo>& highlights_;
   const std::vector<ppapi::PdfAccessibilityTextFieldInfo>& text_fields_;
   const std::vector<ppapi::PdfAccessibilityButtonInfo>& buttons_;
+  const std::vector<ppapi::PdfAccessibilityChoiceFieldInfo>& choice_fields_;
   const gfx::RectF& page_bounds_;
   uint32_t page_index_;
   ui::AXNodeData* page_node_;
diff --git a/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc b/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
index 235291f5..31204ad6 100644
--- a/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
+++ b/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
@@ -865,6 +865,453 @@
   EXPECT_EQ(0u, push_button_node->children().size());
 }
 
+TEST_F(PdfAccessibilityTreeTest, TestListboxNodeCreation) {
+  // Enable feature flag
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      chrome_pdf::features::kAccessiblePDFForm);
+  text_runs_.emplace_back(kFirstTextRun);
+  text_runs_.emplace_back(kSecondTextRun);
+  chars_.insert(chars_.end(), std::begin(kDummyCharsData),
+                std::end(kDummyCharsData));
+
+  struct ListboxOptionInfo {
+    std::string name;
+    bool is_selected;
+  };
+
+  const ListboxOptionInfo kExpectedOptions[][3] = {
+      {{"Alpha", false}, {"Beta", true}, {"Gamma", true}},
+      {{"Foo", false}, {"Bar", true}, {"Qux", false}}};
+
+  const gfx::RectF kExpectedBounds[] = {{1.0f, 1.0f, 5.0f, 6.0f},
+                                        {1.0f, 10.0f, 5.0f, 6.0f}};
+
+  {
+    ppapi::PdfAccessibilityChoiceFieldInfo choice_field;
+    choice_field.bounds = PP_MakeFloatRectFromXYWH(1.0f, 1.0f, 5.0f, 6.0f);
+    choice_field.index_in_page = 0;
+    choice_field.text_run_index = 2;
+    choice_field.type = PP_PRIVATECHOICEFIELD_LISTBOX;
+    choice_field.name = "List Box";
+    choice_field.is_read_only = false;
+    choice_field.is_multi_select = true;
+    choice_field.has_editable_text_box = false;
+    for (const ListboxOptionInfo& expected_option : kExpectedOptions[0]) {
+      ppapi::PdfAccessibilityChoiceFieldOptionInfo choice_field_option;
+      choice_field_option.name = expected_option.name;
+      choice_field_option.is_selected = expected_option.is_selected;
+      choice_field.options.push_back(std::move(choice_field_option));
+    }
+    page_objects_.form_fields.choice_fields.push_back(std::move(choice_field));
+  }
+
+  {
+    ppapi::PdfAccessibilityChoiceFieldInfo choice_field;
+    choice_field.bounds = PP_MakeFloatRectFromXYWH(1.0f, 10.0f, 5.0f, 6.0f);
+    choice_field.index_in_page = 1;
+    choice_field.text_run_index = 2;
+    choice_field.type = PP_PRIVATECHOICEFIELD_LISTBOX;
+    choice_field.name = "Read Only List Box";
+    choice_field.is_read_only = true;
+    choice_field.is_multi_select = false;
+    choice_field.has_editable_text_box = false;
+    for (const ListboxOptionInfo& expected_option : kExpectedOptions[1]) {
+      ppapi::PdfAccessibilityChoiceFieldOptionInfo choice_field_option;
+      choice_field_option.name = expected_option.name;
+      choice_field_option.is_selected = expected_option.is_selected;
+      choice_field.options.push_back(std::move(choice_field_option));
+    }
+    page_objects_.form_fields.choice_fields.push_back(std::move(choice_field));
+  }
+
+  page_info_.text_run_count = text_runs_.size();
+  page_info_.char_count = chars_.size();
+
+  content::RenderFrame* render_frame = view_->GetMainRenderFrame();
+  ASSERT_TRUE(render_frame);
+  render_frame->SetAccessibilityModeForTest(ui::AXMode::kWebContents);
+  ASSERT_TRUE(render_frame->GetRenderAccessibility());
+
+  FakeRendererPpapiHost host(view_->GetMainRenderFrame());
+  PP_Instance instance = 0;
+  pdf::PdfAccessibilityTree pdf_accessibility_tree(&host, instance);
+
+  pdf_accessibility_tree.SetAccessibilityViewportInfo(viewport_info_);
+  pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
+  pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
+                                                  chars_, page_objects_);
+
+  /*
+   * Expected tree structure
+   * Document
+   * ++ Region
+   * ++++ Paragraph
+   * ++++++ Static Text
+   * ++++ Paragraph
+   * ++++++ Static Text
+   * ++++++ Listbox
+   * ++++++++ Listbox Option
+   * ++++++++ Listbox Option
+   * ++++++++ Listbox Option
+   * ++++++ Listbox
+   * ++++++++ Listbox Option
+   * ++++++++ Listbox Option
+   * ++++++++ Listbox Option
+   */
+
+  ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
+  ASSERT_TRUE(root_node);
+  EXPECT_EQ(ax::mojom::Role::kDocument, root_node->data().role);
+  ASSERT_EQ(1u, root_node->children().size());
+
+  ui::AXNode* page_node = root_node->children()[0];
+  ASSERT_TRUE(page_node);
+  EXPECT_EQ(ax::mojom::Role::kRegion, page_node->data().role);
+  ASSERT_EQ(2u, page_node->children().size());
+
+  ui::AXNode* paragraph_node = page_node->children()[0];
+  ASSERT_TRUE(paragraph_node);
+  EXPECT_EQ(ax::mojom::Role::kParagraph, paragraph_node->data().role);
+  ASSERT_EQ(1u, paragraph_node->children().size());
+
+  ui::AXNode* static_text_node = paragraph_node->children()[0];
+  ASSERT_TRUE(static_text_node);
+  EXPECT_EQ(ax::mojom::Role::kStaticText, static_text_node->data().role);
+  ASSERT_EQ(1u, static_text_node->children().size());
+
+  paragraph_node = page_node->children()[1];
+  ASSERT_TRUE(paragraph_node);
+  EXPECT_EQ(ax::mojom::Role::kParagraph, paragraph_node->data().role);
+  const std::vector<ui::AXNode*>& child_nodes = paragraph_node->children();
+  ASSERT_EQ(3u, child_nodes.size());
+
+  static_text_node = child_nodes[0];
+  ASSERT_TRUE(static_text_node);
+  EXPECT_EQ(ax::mojom::Role::kStaticText, static_text_node->data().role);
+  ASSERT_EQ(1u, static_text_node->children().size());
+
+  {
+    ui::AXNode* listbox_node = child_nodes[1];
+    ASSERT_TRUE(listbox_node);
+    EXPECT_EQ(ax::mojom::Role::kListBox, listbox_node->data().role);
+    EXPECT_NE(ax::mojom::Restriction::kReadOnly,
+              listbox_node->data().GetRestriction());
+    EXPECT_EQ("List Box", listbox_node->GetStringAttribute(
+                              ax::mojom::StringAttribute::kName));
+    EXPECT_TRUE(
+        listbox_node->data().HasState(ax::mojom::State::kMultiselectable));
+    EXPECT_TRUE(listbox_node->data().HasState(ax::mojom::State::kFocusable));
+    EXPECT_EQ(kExpectedBounds[0], listbox_node->data().relative_bounds.bounds);
+    ASSERT_EQ(base::size(kExpectedOptions[0]), listbox_node->children().size());
+    const std::vector<ui::AXNode*>& listbox_child_nodes =
+        listbox_node->children();
+    for (size_t i = 0; i < listbox_child_nodes.size(); i++) {
+      EXPECT_EQ(ax::mojom::Role::kListBoxOption,
+                listbox_child_nodes[i]->data().role);
+      EXPECT_NE(ax::mojom::Restriction::kReadOnly,
+                listbox_child_nodes[i]->data().GetRestriction());
+      EXPECT_EQ(kExpectedOptions[0][i].name,
+                listbox_child_nodes[i]->GetStringAttribute(
+                    ax::mojom::StringAttribute::kName));
+      EXPECT_EQ(kExpectedOptions[0][i].is_selected,
+                listbox_child_nodes[i]->GetBoolAttribute(
+                    ax::mojom::BoolAttribute::kSelected));
+      EXPECT_TRUE(listbox_child_nodes[i]->data().HasState(
+          ax::mojom::State::kFocusable));
+      EXPECT_EQ(kExpectedBounds[0],
+                listbox_child_nodes[i]->data().relative_bounds.bounds);
+    }
+  }
+
+  {
+    ui::AXNode* listbox_node = child_nodes[2];
+    ASSERT_TRUE(listbox_node);
+    EXPECT_EQ(ax::mojom::Role::kListBox, listbox_node->data().role);
+    EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
+              listbox_node->data().GetRestriction());
+    EXPECT_EQ("Read Only List Box", listbox_node->GetStringAttribute(
+                                        ax::mojom::StringAttribute::kName));
+    EXPECT_FALSE(
+        listbox_node->data().HasState(ax::mojom::State::kMultiselectable));
+    EXPECT_TRUE(listbox_node->data().HasState(ax::mojom::State::kFocusable));
+    EXPECT_EQ(kExpectedBounds[1], listbox_node->data().relative_bounds.bounds);
+    ASSERT_EQ(base::size(kExpectedOptions[1]), listbox_node->children().size());
+    const std::vector<ui::AXNode*>& listbox_child_nodes =
+        listbox_node->children();
+    for (size_t i = 0; i < listbox_child_nodes.size(); i++) {
+      EXPECT_EQ(ax::mojom::Role::kListBoxOption,
+                listbox_child_nodes[i]->data().role);
+      EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
+                listbox_child_nodes[i]->data().GetRestriction());
+      EXPECT_EQ(kExpectedOptions[1][i].name,
+                listbox_child_nodes[i]->GetStringAttribute(
+                    ax::mojom::StringAttribute::kName));
+      EXPECT_EQ(kExpectedOptions[1][i].is_selected,
+                listbox_child_nodes[i]->GetBoolAttribute(
+                    ax::mojom::BoolAttribute::kSelected));
+      EXPECT_TRUE(listbox_child_nodes[i]->data().HasState(
+          ax::mojom::State::kFocusable));
+      EXPECT_EQ(kExpectedBounds[1],
+                listbox_child_nodes[i]->data().relative_bounds.bounds);
+    }
+  }
+}
+
+TEST_F(PdfAccessibilityTreeTest, TestComboboxNodeCreation) {
+  // Enable feature flag
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      chrome_pdf::features::kAccessiblePDFForm);
+  text_runs_.emplace_back(kFirstTextRun);
+  text_runs_.emplace_back(kSecondTextRun);
+  chars_.insert(chars_.end(), std::begin(kDummyCharsData),
+                std::end(kDummyCharsData));
+
+  struct ComboboxOptionInfo {
+    std::string name;
+    bool is_selected;
+  };
+
+  const ComboboxOptionInfo kExpectedOptions[][3] = {
+      {{"Albania", false}, {"Belgium", true}, {"Croatia", true}},
+      {{"Apple", false}, {"Banana", true}, {"Cherry", false}}};
+
+  const gfx::RectF kExpectedBounds[] = {{1.0f, 1.0f, 5.0f, 6.0f},
+                                        {1.0f, 10.0f, 5.0f, 6.0f}};
+
+  {
+    ppapi::PdfAccessibilityChoiceFieldInfo choice_field;
+    choice_field.bounds = PP_MakeFloatRectFromXYWH(1.0f, 1.0f, 5.0f, 6.0f);
+    choice_field.index_in_page = 0;
+    choice_field.text_run_index = 2;
+    choice_field.type = PP_PRIVATECHOICEFIELD_COMBOBOX;
+    choice_field.name = "Editable Combo Box";
+    choice_field.is_read_only = false;
+    choice_field.is_multi_select = true;
+    choice_field.has_editable_text_box = true;
+    for (const ComboboxOptionInfo& expected_option : kExpectedOptions[0]) {
+      ppapi::PdfAccessibilityChoiceFieldOptionInfo choice_field_option;
+      choice_field_option.name = expected_option.name;
+      choice_field_option.is_selected = expected_option.is_selected;
+      choice_field.options.push_back(std::move(choice_field_option));
+    }
+    page_objects_.form_fields.choice_fields.push_back(std::move(choice_field));
+  }
+
+  {
+    ppapi::PdfAccessibilityChoiceFieldInfo choice_field;
+    choice_field.bounds = PP_MakeFloatRectFromXYWH(1.0f, 10.0f, 5.0f, 6.0f);
+    choice_field.index_in_page = 1;
+    choice_field.text_run_index = 2;
+    choice_field.type = PP_PRIVATECHOICEFIELD_COMBOBOX;
+    choice_field.name = "Read Only Combo Box";
+    choice_field.is_read_only = true;
+    choice_field.is_multi_select = false;
+    choice_field.has_editable_text_box = false;
+    for (const ComboboxOptionInfo& expected_option : kExpectedOptions[1]) {
+      ppapi::PdfAccessibilityChoiceFieldOptionInfo choice_field_option;
+      choice_field_option.name = expected_option.name;
+      choice_field_option.is_selected = expected_option.is_selected;
+      choice_field.options.push_back(std::move(choice_field_option));
+    }
+    page_objects_.form_fields.choice_fields.push_back(std::move(choice_field));
+  }
+
+  page_info_.text_run_count = text_runs_.size();
+  page_info_.char_count = chars_.size();
+
+  content::RenderFrame* render_frame = view_->GetMainRenderFrame();
+  ASSERT_TRUE(render_frame);
+  render_frame->SetAccessibilityModeForTest(ui::AXMode::kWebContents);
+  ASSERT_TRUE(render_frame->GetRenderAccessibility());
+
+  FakeRendererPpapiHost host(view_->GetMainRenderFrame());
+  PP_Instance instance = 0;
+  pdf::PdfAccessibilityTree pdf_accessibility_tree(&host, instance);
+
+  pdf_accessibility_tree.SetAccessibilityViewportInfo(viewport_info_);
+  pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
+  pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
+                                                  chars_, page_objects_);
+  /*
+   * Expected tree structure
+   * Document
+   * ++ Region
+   * ++++ Paragraph
+   * ++++++ Static Text
+   * ++++ Paragraph
+   * ++++++ Static Text
+   * ++++++ Combobox Grouping
+   * ++++++++ Text Field With Combobox
+   * ++++++++ Listbox
+   * ++++++++++ Listbox Option
+   * ++++++++++ Listbox Option
+   * ++++++++++ Listbox Option
+   * ++++++ Combobox Grouping
+   * ++++++++ Combobox Menu Button
+   * ++++++++ Listbox
+   * ++++++++++ Listbox Option
+   * ++++++++++ Listbox Option
+   * ++++++++++ Listbox Option
+   */
+
+  ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
+  ASSERT_TRUE(root_node);
+  EXPECT_EQ(ax::mojom::Role::kDocument, root_node->data().role);
+  ASSERT_EQ(1u, root_node->children().size());
+
+  ui::AXNode* page_node = root_node->children()[0];
+  ASSERT_TRUE(page_node);
+  EXPECT_EQ(ax::mojom::Role::kRegion, page_node->data().role);
+  ASSERT_EQ(2u, page_node->children().size());
+
+  ui::AXNode* paragraph_node = page_node->children()[0];
+  ASSERT_TRUE(paragraph_node);
+  EXPECT_EQ(ax::mojom::Role::kParagraph, paragraph_node->data().role);
+  ASSERT_EQ(1u, paragraph_node->children().size());
+
+  ui::AXNode* static_text_node = paragraph_node->children()[0];
+  ASSERT_TRUE(static_text_node);
+  EXPECT_EQ(ax::mojom::Role::kStaticText, static_text_node->data().role);
+  ASSERT_EQ(1u, static_text_node->children().size());
+
+  paragraph_node = page_node->children()[1];
+  ASSERT_TRUE(paragraph_node);
+  EXPECT_EQ(ax::mojom::Role::kParagraph, paragraph_node->data().role);
+  const std::vector<ui::AXNode*>& child_nodes = paragraph_node->children();
+  ASSERT_EQ(3u, child_nodes.size());
+
+  static_text_node = child_nodes[0];
+  ASSERT_TRUE(static_text_node);
+  EXPECT_EQ(ax::mojom::Role::kStaticText, static_text_node->data().role);
+  ASSERT_EQ(1u, static_text_node->children().size());
+
+  {
+    ui::AXNode* combobox_node = child_nodes[1];
+    ASSERT_TRUE(combobox_node);
+    EXPECT_EQ(ax::mojom::Role::kComboBoxGrouping, combobox_node->data().role);
+    EXPECT_NE(ax::mojom::Restriction::kReadOnly,
+              combobox_node->data().GetRestriction());
+    EXPECT_TRUE(combobox_node->data().HasState(ax::mojom::State::kFocusable));
+    EXPECT_EQ(kExpectedBounds[0], combobox_node->data().relative_bounds.bounds);
+    ASSERT_EQ(2u, combobox_node->children().size());
+    const std::vector<ui::AXNode*>& combobox_child_nodes =
+        combobox_node->children();
+
+    ui::AXNode* combobox_input_node = combobox_child_nodes[0];
+    EXPECT_EQ(ax::mojom::Role::kTextFieldWithComboBox,
+              combobox_input_node->data().role);
+    EXPECT_NE(ax::mojom::Restriction::kReadOnly,
+              combobox_input_node->data().GetRestriction());
+    EXPECT_EQ("Editable Combo Box", combobox_input_node->GetStringAttribute(
+                                        ax::mojom::StringAttribute::kName));
+    EXPECT_EQ("Belgium", combobox_input_node->GetStringAttribute(
+                             ax::mojom::StringAttribute::kValue));
+    EXPECT_TRUE(
+        combobox_input_node->data().HasState(ax::mojom::State::kFocusable));
+    EXPECT_EQ(kExpectedBounds[0],
+              combobox_input_node->data().relative_bounds.bounds);
+
+    ui::AXNode* combobox_popup_node = combobox_child_nodes[1];
+    EXPECT_EQ(ax::mojom::Role::kListBox, combobox_popup_node->data().role);
+    EXPECT_NE(ax::mojom::Restriction::kReadOnly,
+              combobox_popup_node->data().GetRestriction());
+    EXPECT_TRUE(combobox_popup_node->data().HasState(
+        ax::mojom::State::kMultiselectable));
+    EXPECT_EQ(kExpectedBounds[0],
+              combobox_popup_node->data().relative_bounds.bounds);
+    ASSERT_EQ(base::size(kExpectedOptions[0]),
+              combobox_popup_node->children().size());
+    const std::vector<ui::AXNode*>& popup_child_nodes =
+        combobox_popup_node->children();
+    for (size_t i = 0; i < popup_child_nodes.size(); i++) {
+      EXPECT_EQ(ax::mojom::Role::kListBoxOption,
+                popup_child_nodes[i]->data().role);
+      EXPECT_NE(ax::mojom::Restriction::kReadOnly,
+                popup_child_nodes[i]->data().GetRestriction());
+      EXPECT_EQ(kExpectedOptions[0][i].name,
+                popup_child_nodes[i]->GetStringAttribute(
+                    ax::mojom::StringAttribute::kName));
+      EXPECT_EQ(kExpectedOptions[0][i].is_selected,
+                popup_child_nodes[i]->GetBoolAttribute(
+                    ax::mojom::BoolAttribute::kSelected));
+      EXPECT_TRUE(
+          popup_child_nodes[i]->data().HasState(ax::mojom::State::kFocusable));
+      EXPECT_EQ(kExpectedBounds[0],
+                popup_child_nodes[i]->data().relative_bounds.bounds);
+    }
+    EXPECT_EQ(popup_child_nodes[1]->data().id,
+              combobox_input_node->GetIntAttribute(
+                  ax::mojom::IntAttribute::kActivedescendantId));
+    const auto& controls_ids = combobox_input_node->GetIntListAttribute(
+        ax::mojom::IntListAttribute::kControlsIds);
+    ASSERT_EQ(1u, controls_ids.size());
+    EXPECT_EQ(controls_ids[0], combobox_popup_node->data().id);
+  }
+
+  {
+    ui::AXNode* combobox_node = child_nodes[2];
+    ASSERT_TRUE(combobox_node);
+    EXPECT_EQ(ax::mojom::Role::kComboBoxGrouping, combobox_node->data().role);
+    EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
+              combobox_node->data().GetRestriction());
+    EXPECT_TRUE(combobox_node->data().HasState(ax::mojom::State::kFocusable));
+    EXPECT_EQ(kExpectedBounds[1], combobox_node->data().relative_bounds.bounds);
+    ASSERT_EQ(2u, combobox_node->children().size());
+    const std::vector<ui::AXNode*>& combobox_child_nodes =
+        combobox_node->children();
+
+    ui::AXNode* combobox_input_node = combobox_child_nodes[0];
+    EXPECT_EQ(ax::mojom::Role::kComboBoxMenuButton,
+              combobox_input_node->data().role);
+    EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
+              combobox_input_node->data().GetRestriction());
+    EXPECT_EQ("Read Only Combo Box", combobox_input_node->GetStringAttribute(
+                                         ax::mojom::StringAttribute::kName));
+    EXPECT_EQ("Banana", combobox_input_node->GetStringAttribute(
+                            ax::mojom::StringAttribute::kValue));
+    EXPECT_TRUE(
+        combobox_input_node->data().HasState(ax::mojom::State::kFocusable));
+    EXPECT_EQ(kExpectedBounds[1],
+              combobox_input_node->data().relative_bounds.bounds);
+
+    ui::AXNode* combobox_popup_node = combobox_child_nodes[1];
+    EXPECT_EQ(ax::mojom::Role::kListBox, combobox_popup_node->data().role);
+    EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
+              combobox_popup_node->data().GetRestriction());
+    EXPECT_EQ(kExpectedBounds[1],
+              combobox_popup_node->data().relative_bounds.bounds);
+    ASSERT_EQ(base::size(kExpectedOptions[1]),
+              combobox_popup_node->children().size());
+    const std::vector<ui::AXNode*>& popup_child_nodes =
+        combobox_popup_node->children();
+    for (size_t i = 0; i < popup_child_nodes.size(); i++) {
+      EXPECT_EQ(ax::mojom::Role::kListBoxOption,
+                popup_child_nodes[i]->data().role);
+      EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
+                popup_child_nodes[i]->data().GetRestriction());
+      EXPECT_EQ(kExpectedOptions[1][i].name,
+                popup_child_nodes[i]->GetStringAttribute(
+                    ax::mojom::StringAttribute::kName));
+      EXPECT_EQ(kExpectedOptions[1][i].is_selected,
+                popup_child_nodes[i]->GetBoolAttribute(
+                    ax::mojom::BoolAttribute::kSelected));
+      EXPECT_TRUE(
+          popup_child_nodes[i]->data().HasState(ax::mojom::State::kFocusable));
+      EXPECT_EQ(kExpectedBounds[1],
+                popup_child_nodes[i]->data().relative_bounds.bounds);
+    }
+    EXPECT_EQ(popup_child_nodes[1]->data().id,
+              combobox_input_node->GetIntAttribute(
+                  ax::mojom::IntAttribute::kActivedescendantId));
+    const auto& controls_ids = combobox_input_node->GetIntListAttribute(
+        ax::mojom::IntListAttribute::kControlsIds);
+    ASSERT_EQ(1u, controls_ids.size());
+    EXPECT_EQ(controls_ids[0], combobox_popup_node->data().id);
+  }
+}
+
 TEST_F(PdfAccessibilityTreeTest, TestPreviousNextOnLine) {
   text_runs_.emplace_back(kFirstRunMultiLine);
   text_runs_.emplace_back(kSecondRunMultiLine);
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 2e81efe..57d303759 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -4978,7 +4978,7 @@
       'id': 43,
       'caption': '''Default search provider search URL''',
       'tags': ['website-sharing'],
-      'desc': '''If <ph name="DEFAULT_SEARCH_PROVIDER_ENABLED_POLICY_NAME">DefaultSearchProviderEnabled</ph> is on, then setting <ph name="DEFAULT_SEARCH_PROVIDER_SEARCH_URL_POLICY_NAME">DefaultSearchProviderSearchURL</ph> specifies the URL of the search engine used during a default search. The URL should include the string <ph name="SEARCH_TERM_MARKER">'{searchTerms}'</ph>, replaced at the query by the user's search terms.
+      'desc': '''If <ph name="DEFAULT_SEARCH_PROVIDER_ENABLED_POLICY_NAME">DefaultSearchProviderEnabled</ph> is on, then setting <ph name="DEFAULT_SEARCH_PROVIDER_SEARCH_URL_POLICY_NAME">DefaultSearchProviderSearchURL</ph> specifies the URL of the search engine used during a default search. The URL should include the string <ph name="SEARCH_TERM_MARKER">'{searchTerms}'</ph>, replaced in the query by the user's search terms.
 
       You can specify Google's search URL as: <ph name="GOOGLE_SEARCH_URL">'{google:baseURL}search?q={searchTerms}&amp;{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:searchClient}{google:sourceId}ie={inputEncoding}'</ph>.''',
     },
@@ -5001,7 +5001,7 @@
       'id': 44,
       'caption': '''Default search provider suggest URL''',
       'tags': [],
-      'desc': '''If <ph name="DEFAULT_SEARCH_PROVIDER_ENABLED_POLICY_NAME">DefaultSearchProviderEnabled</ph> is on, then setting <ph name="DEFAULT_SEARCH_PROVIDER_SUGGEST_URL_POLICY_NAME">DefaultSearchProviderSuggestURL</ph> specifies the URL of the search engine to provide search suggestions. The URL should include the string <ph name="SEARCH_TERM_MARKER">{searchTerms}</ph>, replaced at the query by the user's search terms.
+      'desc': '''If <ph name="DEFAULT_SEARCH_PROVIDER_ENABLED_POLICY_NAME">DefaultSearchProviderEnabled</ph> is on, then setting <ph name="DEFAULT_SEARCH_PROVIDER_SUGGEST_URL_POLICY_NAME">DefaultSearchProviderSuggestURL</ph> specifies the URL of the search engine to provide search suggestions. The URL should include the string <ph name="SEARCH_TERM_MARKER">'{searchTerms}'</ph>, replaced in the query by the user's search terms.
 
       You can specify Google's search URL as: <ph name="GOOGLE_SUGGEST_SEARCH_URL">'{google:baseURL}complete/search?output=chrome&amp;q={searchTerms}'</ph>.''',
     },
@@ -5166,7 +5166,7 @@
       'id': 237,
       'caption': '''Default search provider new tab page URL''',
       'tags': [],
-      'desc': '''If <ph name="DEFAULT_SEARCH_PROVIDER_ENABLED_POLICY_NAME">DefaultSearchProviderEnabled</ph> is on, then setting <ph name="DEFAULT_SEARCH_PROVIDER_NEW_TAB_URL_POLICY_NAME">DefaultSearchProviderNewTabURL</ph> specifies the URL that a search engine uses to provide a New Tab page.
+      'desc': '''If <ph name="DEFAULT_SEARCH_PROVIDER_ENABLED_POLICY_NAME">DefaultSearchProviderEnabled</ph> is on, then setting <ph name="DEFAULT_SEARCH_PROVIDER_NEW_TAB_URL_POLICY_NAME">DefaultSearchProviderNewTabURL</ph> specifies the URL of the search engine used to provide a New Tab page.
 
       Leaving <ph name="DEFAULT_SEARCH_PROVIDER_NEW_TAB_URL_POLICY_NAME">DefaultSearchProviderNewTabURL</ph> unset means no new tab page is provided.''',
     },
diff --git a/components/previews/content/previews_decider_impl.cc b/components/previews/content/previews_decider_impl.cc
index 86b9e3c1..defeaa5 100644
--- a/components/previews/content/previews_decider_impl.cc
+++ b/components/previews/content/previews_decider_impl.cc
@@ -160,6 +160,11 @@
       previews_data, navigation_handle, is_reload, type, &passed_reasons);
   LogPreviewDecisionMade(eligibility, url, clock_->Now(), type,
                          std::move(passed_reasons), previews_data);
+  if (previews_opt_guide_ &&
+      eligibility == PreviewsEligibilityReason::ALLOWED) {
+    // Kick off model prediction.
+    previews_opt_guide_->StartCheckingIfShouldShowPreview(navigation_handle);
+  }
   return eligibility == PreviewsEligibilityReason::ALLOWED;
 }
 
diff --git a/components/previews/content/previews_optimization_guide.cc b/components/previews/content/previews_optimization_guide.cc
index 1aef72b..4b322fd 100644
--- a/components/previews/content/previews_optimization_guide.cc
+++ b/components/previews/content/previews_optimization_guide.cc
@@ -40,6 +40,10 @@
 // The default max size of the cache holding resource loading hints by URL.
 size_t kDefaultMaxResourceLoadingHintsCacheSize = 10;
 
+// The max size of the cache holding painful page load decisions by the
+// navigation ID of the navigation handle.
+size_t kDefaultPainfulPageLoadDecisionsCacheSize = 10;
+
 // Returns base::nullopt if |previews_type| can't be converted.
 base::Optional<optimization_guide::proto::OptimizationType>
 ConvertPreviewsTypeToOptimizationType(PreviewsType previews_type) {
@@ -100,6 +104,7 @@
     optimization_guide::OptimizationGuideDecider* optimization_guide_decider)
     : optimization_guide_decider_(optimization_guide_decider),
       resource_loading_hints_cache_(kDefaultMaxResourceLoadingHintsCacheSize),
+      painful_page_load_decisions_(kDefaultPainfulPageLoadDecisionsCacheSize),
       registered_optimization_types_(GetOptimizationTypesToRegister()) {
   DCHECK(optimization_guide_decider_);
 
@@ -113,17 +118,50 @@
 
 PreviewsOptimizationGuide::~PreviewsOptimizationGuide() = default;
 
+void PreviewsOptimizationGuide::StartCheckingIfShouldShowPreview(
+    content::NavigationHandle* navigation_handle) {
+  if (params::OverrideShouldShowPreviewCheck()) {
+    // We are not going to use the decision from |optimization_guide_decider_|,
+    // so just return.
+    return;
+  }
+
+  if (painful_page_load_decisions_.Get(navigation_handle->GetNavigationId()) !=
+      painful_page_load_decisions_.end()) {
+    // We have either already evaluated the model or have kicked off a model
+    // evaluation, so just return.
+    return;
+  }
+
+  painful_page_load_decisions_.Put(
+      navigation_handle->GetNavigationId(),
+      optimization_guide::OptimizationGuideDecision::kUnknown);
+  optimization_guide_decider_->ShouldTargetNavigationAsync(
+      navigation_handle,
+      optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
+      /*client_model_features=*/{},
+      base::BindOnce(&PreviewsOptimizationGuide::OnPainfulPageLoadDecision,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     navigation_handle->GetNavigationId()));
+}
+
+void PreviewsOptimizationGuide::OnPainfulPageLoadDecision(
+    int64_t navigation_id,
+    optimization_guide::OptimizationGuideDecision decision) {
+  painful_page_load_decisions_.Put(navigation_id, decision);
+}
+
 bool PreviewsOptimizationGuide::ShouldShowPreview(
     content::NavigationHandle* navigation_handle) {
   // See if we should override the optimization guide and always show a preview.
   if (params::OverrideShouldShowPreviewCheck())
     return true;
 
-  optimization_guide::OptimizationGuideDecision decision =
-      optimization_guide_decider_->ShouldTargetNavigation(
-          navigation_handle,
-          optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  return decision == optimization_guide::OptimizationGuideDecision::kTrue;
+  auto ppd_iter =
+      painful_page_load_decisions_.Get(navigation_handle->GetNavigationId());
+  return ppd_iter != painful_page_load_decisions_.end() &&
+         ppd_iter->second ==
+             optimization_guide::OptimizationGuideDecision::kTrue;
 }
 
 bool PreviewsOptimizationGuide::CanApplyPreview(
diff --git a/components/previews/content/previews_optimization_guide.h b/components/previews/content/previews_optimization_guide.h
index 6248b29..513123b 100644
--- a/components/previews/content/previews_optimization_guide.h
+++ b/components/previews/content/previews_optimization_guide.h
@@ -10,6 +10,7 @@
 
 #include "base/containers/flat_set.h"
 #include "base/containers/mru_cache.h"
+#include "base/memory/weak_ptr.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 
 namespace content {
@@ -20,6 +21,7 @@
 
 namespace optimization_guide {
 class OptimizationGuideDecider;
+enum class OptimizationGuideDecision;
 }  // namespace optimization_guide
 
 namespace previews {
@@ -37,6 +39,11 @@
       delete;
   virtual ~PreviewsOptimizationGuide();
 
+  // Kicks off the call to |optimization_guide_decider_| for whether a Preview
+  // should be shown for the current conditions.
+  virtual void StartCheckingIfShouldShowPreview(
+      content::NavigationHandle* navigation_handle);
+
   // Returns whether a Preview should be shown for the current conditions.
   virtual bool ShouldShowPreview(content::NavigationHandle* navigation_handle);
 
@@ -54,6 +61,13 @@
       std::vector<std::string>* out_resource_patterns_to_block);
 
  private:
+  // Invoked when |optimization_guide_decider_| sends the decision for whether
+  // the page load for the navigation, as expressed by |navigation_id|, is
+  // painful or not.
+  void OnPainfulPageLoadDecision(
+      int64_t navigation_id,
+      optimization_guide::OptimizationGuideDecision decision);
+
   // The Optimization Guide Decider to consult for whether an optimization can
   // be applied. Not owned.
   optimization_guide::OptimizationGuideDecider* optimization_guide_decider_;
@@ -62,9 +76,16 @@
   // us to avoid making too many calls to |optimization_guide_decider_|.
   base::MRUCache<GURL, std::vector<std::string>> resource_loading_hints_cache_;
 
+  // An in-memory cache of painful page load decisions keyed by the navigation
+  // ID of the navigation handle that the decision was evaluated on.
+  base::MRUCache<int64_t, optimization_guide::OptimizationGuideDecision>
+      painful_page_load_decisions_;
+
   // The optimization types registered with |optimization_guide_decider_|.
   const base::flat_set<optimization_guide::proto::OptimizationType>
       registered_optimization_types_;
+
+  base::WeakPtrFactory<PreviewsOptimizationGuide> weak_ptr_factory_{this};
 };
 
 }  // namespace previews
diff --git a/components/previews/content/previews_optimization_guide_unittest.cc b/components/previews/content/previews_optimization_guide_unittest.cc
index fca7192..11c501e 100644
--- a/components/previews/content/previews_optimization_guide_unittest.cc
+++ b/components/previews/content/previews_optimization_guide_unittest.cc
@@ -61,22 +61,32 @@
     return registered_optimization_targets_;
   }
 
-  optimization_guide::OptimizationGuideDecision ShouldTargetNavigation(
+  void ShouldTargetNavigationAsync(
       content::NavigationHandle* navigation_handle,
-      optimization_guide::proto::OptimizationTarget optimization_target)
+      optimization_guide::proto::OptimizationTarget optimization_target,
+      const base::flat_map<optimization_guide::proto::ClientModelFeature,
+                           float>& client_model_features,
+      optimization_guide::OptimizationGuideTargetDecisionCallback callback)
       override {
     // This method should always be called with the painful page load target.
     EXPECT_EQ(optimization_target,
               optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+    // We expect that the opt guide will calculate all features for us and we
+    // will not override with anything.
+    EXPECT_TRUE(client_model_features.empty());
 
     net::EffectiveConnectionType ect =
         network_quality_tracker_.GetEffectiveConnectionType();
     if (ect == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
-      return optimization_guide::OptimizationGuideDecision::kUnknown;
+      std::move(callback).Run(
+          optimization_guide::OptimizationGuideDecision::kUnknown);
+    } else if (ect <= net::EFFECTIVE_CONNECTION_TYPE_2G) {
+      std::move(callback).Run(
+          optimization_guide::OptimizationGuideDecision::kTrue);
+    } else {
+      std::move(callback).Run(
+          optimization_guide::OptimizationGuideDecision::kFalse);
     }
-    if (ect <= net::EFFECTIVE_CONNECTION_TYPE_2G)
-      return optimization_guide::OptimizationGuideDecision::kTrue;
-    return optimization_guide::OptimizationGuideDecision::kFalse;
   }
 
   optimization_guide::OptimizationGuideDecision CanApplyOptimization(
@@ -373,6 +383,10 @@
   content::MockNavigationHandle navigation_handle;
   navigation_handle.set_url(GURL("doesntmatter"));
 
+  // This should be a no-op, but call it just to make sure any decisions are
+  // overridden.
+  guide.StartCheckingIfShouldShowPreview(&navigation_handle);
+
   EXPECT_TRUE(guide.ShouldShowPreview(&navigation_handle));
 }
 
@@ -393,6 +407,8 @@
   content::MockNavigationHandle navigation_handle;
   navigation_handle.set_url(GURL("doesntmatter"));
 
+  guide.StartCheckingIfShouldShowPreview(&navigation_handle);
+
   EXPECT_FALSE(guide.ShouldShowPreview(&navigation_handle));
 }
 
@@ -406,6 +422,8 @@
   content::MockNavigationHandle navigation_handle;
   navigation_handle.set_url(GURL("doesntmatter"));
 
+  guide.StartCheckingIfShouldShowPreview(&navigation_handle);
+
   EXPECT_TRUE(guide.ShouldShowPreview(&navigation_handle));
 }
 
@@ -419,6 +437,8 @@
   content::MockNavigationHandle navigation_handle;
   navigation_handle.set_url(GURL("doesntmatter"));
 
+  guide.StartCheckingIfShouldShowPreview(&navigation_handle);
+
   EXPECT_FALSE(guide.ShouldShowPreview(&navigation_handle));
 }
 
@@ -432,6 +452,8 @@
   content::MockNavigationHandle navigation_handle;
   navigation_handle.set_url(GURL("doesntmatter"));
 
+  guide.StartCheckingIfShouldShowPreview(&navigation_handle);
+
   EXPECT_FALSE(guide.ShouldShowPreview(&navigation_handle));
 }
 
diff --git a/components/test/data/history/Favicons.v3.sql b/components/test/data/history/Favicons.v3.sql
index 1cdfc6f..c78c4c6 100644
--- a/components/test/data/history/Favicons.v3.sql
+++ b/components/test/data/history/Favicons.v3.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.Version3
+-- unit_tests --gtest_filter=FaviconDatabaseTest.Version3
 --
 -- .dump of a version 3 Favicons database.  Version 3 contained a
 -- table [thumbnails] which was migrated to [Top Sites] database, and
diff --git a/components/test/data/history/Favicons.v4.sql b/components/test/data/history/Favicons.v4.sql
index d2549bf2..952e6a0 100644
--- a/components/test/data/history/Favicons.v4.sql
+++ b/components/test/data/history/Favicons.v4.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.Version4
+-- unit_tests --gtest_filter=FaviconDatabaseTest.Version4
 --
 -- .dump of a version 4 Favicons database.
 BEGIN TRANSACTION;
diff --git a/components/test/data/history/Favicons.v5.sql b/components/test/data/history/Favicons.v5.sql
index 655d2824..fcd01b20 100644
--- a/components/test/data/history/Favicons.v5.sql
+++ b/components/test/data/history/Favicons.v5.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.Version5
+-- unit_tests --gtest_filter=FaviconDatabaseTest.Version5
 --
 -- .dump of a version 5 Favicons database.
 BEGIN TRANSACTION;
diff --git a/components/test/data/history/Favicons.v6.sql b/components/test/data/history/Favicons.v6.sql
index c27125b0..27fa35a 100644
--- a/components/test/data/history/Favicons.v6.sql
+++ b/components/test/data/history/Favicons.v6.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.Version6
+-- unit_tests --gtest_filter=FaviconDatabaseTest.Version6
 --
 -- .dump of a version 6 Favicons database.
 BEGIN TRANSACTION;
diff --git a/components/test/data/history/Favicons.v7.sql b/components/test/data/history/Favicons.v7.sql
index 8b0fd2c..cee69d7 100644
--- a/components/test/data/history/Favicons.v7.sql
+++ b/components/test/data/history/Favicons.v7.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.Version7
+-- unit_tests --gtest_filter=FaviconDatabaseTest.Version7
 --
 -- .dump of a version 7 Favicons database.
 BEGIN TRANSACTION;
diff --git a/components/test/data/history/Favicons.v8.sql b/components/test/data/history/Favicons.v8.sql
index 0905172..cfd2c885 100644
--- a/components/test/data/history/Favicons.v8.sql
+++ b/components/test/data/history/Favicons.v8.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.Version8
+-- unit_tests --gtest_filter=FaviconDatabaseTest.Version8
 --
 -- .dump of a version 8 Favicons database.
 BEGIN TRANSACTION;
diff --git a/components/test/data/history/thumbnail_wild/Favicons.corrupt_meta.disable b/components/test/data/history/thumbnail_wild/Favicons.corrupt_meta.disable
index 05e46ef..1fc8e35 100644
--- a/components/test/data/history/thumbnail_wild/Favicons.corrupt_meta.disable
+++ b/components/test/data/history/thumbnail_wild/Favicons.corrupt_meta.disable
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.WildSchema
+-- unit_tests --gtest_filter=FaviconDatabaseTest.WildSchema
 --
 -- [meta] table has first column [oey] instead of [key] (single-bit
 -- error).  Database consistent with v6, but that may just mean that
diff --git a/components/test/data/history/thumbnail_wild/Favicons.v2.init.sql b/components/test/data/history/thumbnail_wild/Favicons.v2.init.sql
index 53b6b730..3f46853 100644
--- a/components/test/data/history/thumbnail_wild/Favicons.v2.init.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v2.init.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.WildSchema
+-- unit_tests --gtest_filter=FaviconDatabaseTest.WildSchema
 --
 -- Based on version 2 schema found in the wild by error diagnostics.
 -- The schema was failing to migrate because the current-version
diff --git a/components/test/data/history/thumbnail_wild/Favicons.v3.init.sql b/components/test/data/history/thumbnail_wild/Favicons.v3.init.sql
index 3453860..1f8dfb4 100644
--- a/components/test/data/history/thumbnail_wild/Favicons.v3.init.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v3.init.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.WildSchema
+-- unit_tests --gtest_filter=FaviconDatabaseTest.WildSchema
 --
 -- Based on version 3 schema found in the wild by error diagnostics.
 -- The schema was failing to migrate because the current-version
diff --git a/components/test/data/history/thumbnail_wild/Favicons.v4.init.sql b/components/test/data/history/thumbnail_wild/Favicons.v4.init.sql
index 6c5738e6..5c938b4 100644
--- a/components/test/data/history/thumbnail_wild/Favicons.v4.init.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v4.init.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.WildSchema
+-- unit_tests --gtest_filter=FaviconDatabaseTest.WildSchema
 --
 -- Based on version 3 and 4 schema found in the wild by error
 -- diagnostics.  Combined because they were the same except for
diff --git a/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type.sql b/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type.sql
index a00a4398..6545fd9 100644
--- a/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.WildSchema
+-- unit_tests --gtest_filter=FaviconDatabaseTest.WildSchema
 --
 -- Based on version 5 schema found in the wild by error diagnostics.
 -- The schema failed to open because the v3 [favicons] table is
diff --git a/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type2.sql b/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type2.sql
index 25cedede..377b0e9 100644
--- a/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type2.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v5.icon_type2.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.WildSchema
+-- unit_tests --gtest_filter=FaviconDatabaseTest.WildSchema
 --
 -- Based on version 5 schema found in the wild by error diagnostics.
 -- The schema failed to open because the v3 [favicons] table is
diff --git a/components/test/data/history/thumbnail_wild/Favicons.v5.init.sql b/components/test/data/history/thumbnail_wild/Favicons.v5.init.sql
index c92a1108..8c1832b 100644
--- a/components/test/data/history/thumbnail_wild/Favicons.v5.init.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v5.init.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.WildSchema
+-- unit_tests --gtest_filter=FaviconDatabaseTest.WildSchema
 --
 -- Based on version 5 schema found in the wild by error diagnostics.
 -- The schema failed migrations because of an unexpected
diff --git a/components/test/data/history/thumbnail_wild/Favicons.v5.sizes.sql b/components/test/data/history/thumbnail_wild/Favicons.v5.sizes.sql
index 6dfa9e9d..9c54ff0 100644
--- a/components/test/data/history/thumbnail_wild/Favicons.v5.sizes.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v5.sizes.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.WildSchema
+-- unit_tests --gtest_filter=FaviconDatabaseTest.WildSchema
 --
 -- Based on version 5 schema found in the wild by error diagnostics.
 -- The schema failed to open because the v3 [favicons] table is
diff --git a/components/test/data/history/thumbnail_wild/Favicons.v6.init.sql b/components/test/data/history/thumbnail_wild/Favicons.v6.init.sql
index 3014c376..aaaa036 100644
--- a/components/test/data/history/thumbnail_wild/Favicons.v6.init.sql
+++ b/components/test/data/history/thumbnail_wild/Favicons.v6.init.sql
@@ -1,4 +1,4 @@
--- unit_tests --gtest_filter=ThumbnailDatabaseTest.WildSchema
+-- unit_tests --gtest_filter=FaviconDatabaseTest.WildSchema
 --
 -- Based on version 6 schema found in the wild by error diagnostics.
 -- The schema failed migrations because of unexpected
diff --git a/components/user_prefs/android/java/src/org/chromium/components/user_prefs/UserPrefs.java b/components/user_prefs/android/java/src/org/chromium/components/user_prefs/UserPrefs.java
index f92ed89..a0055e6 100644
--- a/components/user_prefs/android/java/src/org/chromium/components/user_prefs/UserPrefs.java
+++ b/components/user_prefs/android/java/src/org/chromium/components/user_prefs/UserPrefs.java
@@ -4,6 +4,8 @@
 
 package org.chromium.components.user_prefs;
 
+import androidx.annotation.VisibleForTesting;
+
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.components.embedder_support.browser_context.BrowserContextHandle;
@@ -20,8 +22,9 @@
         return UserPrefsJni.get().get(browserContextHandle);
     }
 
+    @VisibleForTesting
     @NativeMethods
-    interface Natives {
+    public interface Natives {
         PrefService get(BrowserContextHandle browserContextHandle);
     }
 }
diff --git a/content/browser/browsing_instance.cc b/content/browser/browsing_instance.cc
index d7e1a9e..54546d1 100644
--- a/content/browser/browsing_instance.cc
+++ b/content/browser/browsing_instance.cc
@@ -60,9 +60,8 @@
   return site_url_set_.find(site_url) != site_url_set_.end();
 }
 
-bool BrowsingInstance::HasSiteInstance(const GURL& url) {
-  std::string site = GetSiteInfoForURL(url).site_url().possibly_invalid_spec();
-  return site_instance_map_.find(site) != site_instance_map_.end();
+bool BrowsingInstance::HasSiteInstance(const SiteInfo& site_info) {
+  return site_instance_map_.find(site_info) != site_instance_map_.end();
 }
 
 scoped_refptr<SiteInstanceImpl> BrowsingInstance::GetSiteInstanceForURL(
@@ -117,16 +116,16 @@
 scoped_refptr<SiteInstanceImpl> BrowsingInstance::GetSiteInstanceForURLHelper(
     const GURL& url,
     bool allow_default_instance) {
-  const GURL site_url = GetSiteInfoForURL(url).site_url();
-  auto i = site_instance_map_.find(site_url.possibly_invalid_spec());
+  const SiteInfo site_info = GetSiteInfoForURL(url);
+  auto i = site_instance_map_.find(site_info);
   if (i != site_instance_map_.end())
     return i->second;
 
   // Check to see if we can use the default SiteInstance for sites that don't
   // need to be isolated in their own process.
   if (allow_default_instance &&
-      SiteInstanceImpl::CanBePlacedInDefaultSiteInstance(isolation_context_,
-                                                         url, site_url)) {
+      SiteInstanceImpl::CanBePlacedInDefaultSiteInstance(
+          isolation_context_, url, site_info.site_url())) {
     DCHECK(!default_process_);
     scoped_refptr<SiteInstanceImpl> site_instance = default_site_instance_;
     if (!site_instance) {
@@ -146,7 +145,7 @@
 
     // Add |site_url| to the set so we can keep track of all the sites the
     // the default SiteInstance has been returned for.
-    site_url_set_.insert(site_url);
+    site_url_set_.insert(site_info.site_url());
     return site_instance;
   }
 
@@ -162,17 +161,17 @@
   if (site_instance == default_site_instance_)
     return;
 
-  std::string site = site_instance->GetSiteURL().possibly_invalid_spec();
+  const SiteInfo& site_info = site_instance->GetSiteInfo();
 
   // Only register if we don't have a SiteInstance for this site already.
   // It's possible to have two SiteInstances point to the same site if two
   // tabs are navigated there at the same time.  (We don't call SetSite or
   // register them until DidNavigate.)  If there is a previously existing
   // SiteInstance for this site, we just won't register the new one.
-  auto i = site_instance_map_.find(site);
+  auto i = site_instance_map_.find(site_info);
   if (i == site_instance_map_.end()) {
     // Not previously registered, so register it.
-    site_instance_map_[site] = site_instance;
+    site_instance_map_[site_info] = site_instance;
   }
 }
 
@@ -185,12 +184,10 @@
     default_site_instance_ = nullptr;
   }
 
-  std::string site = site_instance->GetSiteURL().possibly_invalid_spec();
-
   // Only unregister the SiteInstance if it is the same one that is registered
   // for the site.  (It might have been an unregistered SiteInstance.  See the
   // comments in RegisterSiteInstance.)
-  auto i = site_instance_map_.find(site);
+  auto i = site_instance_map_.find(site_instance->GetSiteInfo());
   if (i != site_instance_map_.end() && i->second == site_instance) {
     // Matches, so erase it.
     site_instance_map_.erase(i);
diff --git a/content/browser/browsing_instance.h b/content/browser/browsing_instance.h
index d2e734c2..7f58845 100644
--- a/content/browser/browsing_instance.h
+++ b/content/browser/browsing_instance.h
@@ -8,7 +8,6 @@
 #include <stddef.h>
 
 #include <string>
-#include <unordered_map>
 
 #include "base/check_op.h"
 #include "base/gtest_prod_util.h"
@@ -99,8 +98,8 @@
   const IsolationContext& isolation_context() { return isolation_context_; }
 
   // Returns whether this BrowsingInstance has registered a SiteInstance for
-  // the site of the given URL.
-  bool HasSiteInstance(const GURL& url);
+  // the site of |site_info|.
+  bool HasSiteInstance(const SiteInfo& site_info);
 
   // Get the SiteInstance responsible for rendering the given URL.  Should
   // create a new one if necessary, but should not create more than one
@@ -183,11 +182,10 @@
   // Note: This should not be used by code outside this class.
   SiteInfo GetSiteInfoForURL(const GURL& url) const;
 
-  // Map of site to SiteInstance, to ensure we only have one SiteInstance per
-  // site.
-  // TODO(wjmaclean): This map will be updated in CL#3 as described in
-  // https://crbug.com/1085275#c2 to use a SiteInfo hashkey as the key string.
-  typedef std::unordered_map<std::string, SiteInstanceImpl*> SiteInstanceMap;
+  // Map of SiteInfo to SiteInstance, to ensure we only have one SiteInstance
+  // per SiteInfo. See https://crbug.com/1085275#c2 for the rationale behind
+  // why SiteInfo is the right class to key this on.
+  typedef std::map<SiteInfo, SiteInstanceImpl*> SiteInstanceMap;
 
   // The next available browser-global BrowsingInstance ID.
   static int next_browsing_instance_id_;
@@ -228,7 +226,7 @@
 
   // Keeps track of the site URLs that this object mapped to the
   // |default_site_instance_|.
-  // TODO(wjmaclean): convert this to use a SiteInfo hash key instead.
+  // TODO(wjmaclean): Revise this to store SiteInfos instead of GURLs.
   std::set<GURL> site_url_set_;
 
   DISALLOW_COPY_AND_ASSIGN(BrowsingInstance);
diff --git a/content/browser/code_cache/generated_code_cache.cc b/content/browser/code_cache/generated_code_cache.cc
index dd5c28f9..69d1f92 100644
--- a/content/browser/code_cache/generated_code_cache.cc
+++ b/content/browser/code_cache/generated_code_cache.cc
@@ -54,6 +54,8 @@
 // with a separator in between. |origin_lock| could be empty when renderer is
 // not locked to an origin (ex: SitePerProcess is disabled) and it is safe to
 // use only |resource_url| as the key in such cases.
+// TODO(wjmaclean): Either convert this to use a SiteInfo object, or convert it
+// to something not based on URLs.
 std::string GetCacheKey(const GURL& resource_url, const GURL& origin_lock) {
   CheckValidKeys(resource_url, origin_lock);
 
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 928696e7..d4f5a646 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -1691,7 +1691,7 @@
             current_instance_impl->GetBrowserContext(), dest_site_info) &&
         RenderProcessHostImpl::GetSoleProcessHostForURL(
             current_instance_impl->GetIsolationContext(), dest_url);
-    if (current_instance_impl->HasRelatedSiteInstance(dest_url) ||
+    if (current_instance_impl->HasRelatedSiteInstance(dest_site_info) ||
         use_process_per_site) {
       return SiteInstanceDescriptor(dest_url, SiteInstanceRelation::RELATED);
     }
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 78a6a81..07431d61 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -11,7 +11,6 @@
 #include <limits>
 #include <map>
 #include <set>
-#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -344,21 +343,21 @@
 // site in process-per-site mode.  Each map is specific to a BrowserContext.
 class SiteProcessMap : public base::SupportsUserData::Data {
  public:
-  typedef std::unordered_map<std::string, RenderProcessHost*> SiteToProcessMap;
-  SiteProcessMap() {}
+  typedef std::map<SiteInfo, RenderProcessHost*> SiteToProcessMap;
+  SiteProcessMap() = default;
 
-  void RegisterProcess(const std::string& site, RenderProcessHost* process) {
+  void RegisterProcess(const SiteInfo& site_info, RenderProcessHost* process) {
     // There could already exist a site to process mapping due to races between
     // two WebContents with blank SiteInstances. If that occurs, keeping the
     // existing entry and not overwriting it is a predictable behavior that is
     // safe.
-    auto i = map_.find(site);
+    auto i = map_.find(site_info);
     if (i == map_.end())
-      map_[site] = process;
+      map_[site_info] = process;
   }
 
-  RenderProcessHost* FindProcess(const std::string& site) {
-    auto i = map_.find(site);
+  RenderProcessHost* FindProcess(const SiteInfo& site_info) {
+    auto i = map_.find(site_info);
     if (i != map_.end())
       return i->second;
     return nullptr;
@@ -367,13 +366,13 @@
   void RemoveProcess(RenderProcessHost* host) {
     // Find all instances of this process in the map, then separately remove
     // them.
-    std::set<std::string> sites;
+    std::set<SiteInfo> site_info_set;
     for (SiteToProcessMap::const_iterator i = map_.begin(); i != map_.end();
          ++i) {
       if (i->second == host)
-        sites.insert(i->first);
+        site_info_set.insert(i->first);
     }
-    for (auto i = sites.begin(); i != sites.end(); ++i) {
+    for (auto i = site_info_set.begin(); i != site_info_set.end(); ++i) {
       auto iter = map_.find(*i);
       if (iter != map_.end()) {
         DCHECK_EQ(iter->second, host);
@@ -797,7 +796,7 @@
 
   void IncrementSiteProcessCount(const SiteInfo& site_info,
                                  int render_process_host_id) {
-    std::map<ProcessID, Count>& counts_per_process = map_[site_info.site_url()];
+    std::map<ProcessID, Count>& counts_per_process = map_[site_info];
     ++counts_per_process[render_process_host_id];
 
 #ifndef NDEBUG
@@ -811,7 +810,7 @@
 
   void DecrementSiteProcessCount(const SiteInfo& site_info,
                                  int render_process_host_id) {
-    auto result = map_.find(site_info.site_url());
+    auto result = map_.find(site_info);
     DCHECK(result != map_.end());
     std::map<ProcessID, Count>& counts_per_process = result->second;
 
@@ -822,14 +821,14 @@
       counts_per_process.erase(render_process_host_id);
 
     if (counts_per_process.empty())
-      map_.erase(site_info.site_url());
+      map_.erase(site_info);
   }
 
   void FindRenderProcessesForSiteInstance(
       SiteInstanceImpl* site_instance,
       std::set<RenderProcessHost*>* foreground_processes,
       std::set<RenderProcessHost*>* background_processes) {
-    auto result = map_.find(site_instance->GetSiteInfo().site_url());
+    auto result = map_.find(site_instance->GetSiteInfo());
     if (result == map_.end())
       return;
 
@@ -876,8 +875,8 @@
       // only have a site URL here.  For now, this mismatch is ok since
       // ShouldAssignSiteForURL() only cares about schemes in practice, but
       // this should be cleaned up.
-      if (!SiteInstanceImpl::ShouldAssignSiteForURL(iter.first) &&
-          !iter.first.IsAboutBlank() &&
+      if (!SiteInstanceImpl::ShouldAssignSiteForURL(iter.first.site_url()) &&
+          !iter.first.site_url().IsAboutBlank() &&
           base::Contains(iter.second, host->GetID()))
         return true;
     }
@@ -909,8 +908,8 @@
 
   using ProcessID = int;
   using Count = int;
-  // TODO(wjmaclean): Convert the key from GURL to a SiteInfo hashkey.
-  using CountPerProcessPerSiteMap = std::map<GURL, std::map<ProcessID, Count>>;
+  using CountPerProcessPerSiteMap =
+      std::map<SiteInfo, std::map<ProcessID, Count>>;
   CountPerProcessPerSiteMap map_;
 };
 
@@ -1029,16 +1028,15 @@
 
  private:
   using ProcessID = int;
-  // TODO(wjmaclean): Convert the pair to use SiteInfo hashkey instead of GURL.
-  using SiteProcessIDPair = std::pair<GURL, ProcessID>;
+  using SiteProcessIDPair = std::pair<SiteInfo, ProcessID>;
   using SiteProcessIDPairSet = std::set<SiteProcessIDPair>;
 
   void RegisterProcessForSite(RenderProcessHost* host,
                               SiteInstanceImpl* site_instance) {
     if (!HasProcess(host))
       host->AddObserver(this);
-    site_process_set_.insert(SiteProcessIDPair(
-        site_instance->GetSiteInfo().site_url(), host->GetID()));
+    site_process_set_.insert(
+        SiteProcessIDPair(site_instance->GetSiteInfo(), host->GetID()));
   }
 
   RenderProcessHost* TakeFreshestProcessForSite(
@@ -1079,13 +1077,13 @@
       // default SiteInstance. This allows the default SiteInstance to reuse a
       // service worker process for any site that has been associated with it.
       for (const auto& site_process_pair : reversed_site_process_set) {
-        if (site_instance->IsSiteInDefaultSiteInstance(site_process_pair.first))
+        if (site_instance->IsSiteInDefaultSiteInstance(
+                site_process_pair.first.site_url()))
           return site_process_pair;
       }
     } else {
-      const GURL site_url(site_instance->GetSiteInfo().site_url());
       for (const auto& site_process_pair : reversed_site_process_set) {
-        if (site_process_pair.first == site_url)
+        if (site_process_pair.first == site_instance->GetSiteInfo())
           return site_process_pair;
       }
     }
@@ -4329,9 +4327,8 @@
 
   // See if we have an existing process with appropriate bindings for this
   // site. If not, the caller should create a new process and register it.
-  // Note that IsSuitableHost expects a site URL rather than the full |url|.
-  const GURL& site_url = site_info.site_url();
-  RenderProcessHost* host = map->FindProcess(site_url.possibly_invalid_spec());
+  // Note that IsSuitableHost expects a SiteInfo rather than the full |url|.
+  RenderProcessHost* host = map->FindProcess(site_info);
   if (host && (!host->MayReuseHost() ||
                !IsSuitableHost(host, isolation_context, site_info, is_guest))) {
     // The registered process does not have an appropriate set of bindings for
@@ -4359,10 +4356,8 @@
   // use process-per-site mode.  We cannot check whether the process has
   // appropriate bindings here, because the bindings have not yet been
   // granted.
-  std::string site =
-      site_instance->GetSiteInfo().site_url().possibly_invalid_spec();
-  if (!site.empty())
-    map->RegisterProcess(site, process);
+  if (!site_instance->GetSiteInfo().is_empty())
+    map->RegisterProcess(site_instance->GetSiteInfo(), process);
 }
 
 // static
diff --git a/content/browser/screen_enumeration/screen_enumeration_browsertest.cc b/content/browser/screen_enumeration/screen_enumeration_browsertest.cc
index 0efc05709..daa5f9cb 100644
--- a/content/browser/screen_enumeration/screen_enumeration_browsertest.cc
+++ b/content/browser/screen_enumeration/screen_enumeration_browsertest.cc
@@ -19,12 +19,12 @@
 
 namespace {
 
-// Used to get info about the screens in a list of dictionary values.
+// Used to get async getScreens() info in a list of dictionary values.
 constexpr char kGetScreensScript[] = R"(
   (async () => {
     const screens = await self.getScreens();
     let result = [];
-    for (s of screens) {
+    for (let s of screens) {
       result.push({ availHeight: s.availHeight,
                     availLeft: s.availLeft,
                     availTop: s.availTop,
@@ -46,6 +46,11 @@
   })();
 )";
 
+// Used to get the async result of isMultiScreen().
+constexpr char kIsMultiScreenScript[] = R"(
+  (async () => { return await self.isMultiScreen(); })();
+)";
+
 // Returns a list of dictionary values from native screen information, intended
 // for comparison with the result of kGetScreensScript.
 base::ListValue GetExpectedScreens() {
@@ -107,6 +112,14 @@
   EXPECT_EQ(GetExpectedScreens(), base::Value::AsListValue(result.value));
 }
 
+IN_PROC_BROWSER_TEST_F(ScreenEnumerationTest, IsMultiScreenBasic) {
+  ASSERT_TRUE(NavigateToURL(shell(), GetTestUrl(nullptr, "empty.html")));
+  ASSERT_EQ(true, EvalJs(shell()->web_contents(), "'isMultiScreen' in self"));
+  auto result = EvalJs(shell()->web_contents(), kIsMultiScreenScript);
+  EXPECT_EQ(display::Screen::GetScreen()->GetNumDisplays() > 1,
+            result.ExtractBool());
+}
+
 // Tests screen enumeration functionality with a fake Screen object.
 class FakeScreenEnumerationTest : public ScreenEnumerationTest {
  public:
@@ -153,7 +166,7 @@
   ASSERT_EQ(true, EvalJs(test_shell()->web_contents(), "'getScreens' in self"));
 
   screen()->display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
-                                      display::DisplayList::Type::PRIMARY);
+                                      display::DisplayList::Type::NOT_PRIMARY);
   screen()->display_list().AddDisplay({2, gfx::Rect(901, 100, 801, 802)},
                                       display::DisplayList::Type::NOT_PRIMARY);
 
@@ -164,6 +177,27 @@
 // TODO(crbug.com/1042990): Windows crashes static casting to ScreenWin.
 // TODO(crbug.com/1042990): Android requires a GetDisplayNearestView overload.
 #if defined(OS_ANDROID) || defined(OS_WIN)
+#define MAYBE_IsMultiScreenFaked DISABLED_IsMultiScreenFaked
+#else
+#define MAYBE_IsMultiScreenFaked IsMultiScreenFaked
+#endif
+IN_PROC_BROWSER_TEST_F(FakeScreenEnumerationTest, MAYBE_IsMultiScreenFaked) {
+  ASSERT_TRUE(NavigateToURL(test_shell(), GetTestUrl(nullptr, "empty.html")));
+  ASSERT_EQ(true,
+            EvalJs(test_shell()->web_contents(), "'isMultiScreen' in self"));
+  EXPECT_EQ(false, EvalJs(test_shell()->web_contents(), kIsMultiScreenScript));
+
+  screen()->display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
+                                      display::DisplayList::Type::NOT_PRIMARY);
+  EXPECT_EQ(true, EvalJs(test_shell()->web_contents(), kIsMultiScreenScript));
+
+  screen()->display_list().RemoveDisplay(1);
+  EXPECT_EQ(false, EvalJs(test_shell()->web_contents(), kIsMultiScreenScript));
+}
+
+// TODO(crbug.com/1042990): Windows crashes static casting to ScreenWin.
+// TODO(crbug.com/1042990): Android requires a GetDisplayNearestView overload.
+#if defined(OS_ANDROID) || defined(OS_WIN)
 #define MAYBE_OnScreensChange DISABLED_OnScreensChange
 #else
 #define MAYBE_OnScreensChange OnScreensChange
diff --git a/content/browser/screen_enumeration/screen_enumeration_impl.cc b/content/browser/screen_enumeration/screen_enumeration_impl.cc
index 766d3d0..4a07f569 100644
--- a/content/browser/screen_enumeration/screen_enumeration_impl.cc
+++ b/content/browser/screen_enumeration/screen_enumeration_impl.cc
@@ -28,9 +28,8 @@
 
 void ScreenEnumerationImpl::GetDisplays(GetDisplaysCallback callback) {
   // Ensure the callback is run if this object is prematurely destroyed.
-  auto scoped_callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
-      std::move(callback), std::vector<display::Display>(),
-      display::kInvalidDisplayId, display::kInvalidDisplayId, false);
+  auto scoped_callback =
+      mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), nullptr);
 
   auto* permission_controller = PermissionControllerImpl::FromBrowserContext(
       render_frame_host_->GetProcess()->GetBrowserContext());
@@ -46,18 +45,26 @@
     GetDisplaysCallback callback,
     blink::mojom::PermissionStatus permission_status) {
   if (permission_status != blink::mojom::PermissionStatus::GRANTED) {
-    std::move(callback).Run({}, display::kInvalidDisplayId,
-                            display::kInvalidDisplayId, false);
+    std::move(callback).Run(nullptr);
     return;
   }
 
   display::Screen* screen = display::Screen::GetScreen();
-  const std::vector<display::Display> displays = screen->GetAllDisplays();
-  const int64_t internal_id = display::Display::HasInternalDisplay()
-                                  ? display::Display::InternalDisplayId()
-                                  : display::kInvalidDisplayId;
-  const int64_t primary_id = screen->GetPrimaryDisplay().id();
-  std::move(callback).Run(std::move(displays), internal_id, primary_id, true);
+  auto result = blink::mojom::Displays::New();
+  result->displays = screen->GetAllDisplays();
+  result->internal_id = display::Display::HasInternalDisplay()
+                            ? display::Display::InternalDisplayId()
+                            : display::kInvalidDisplayId;
+  result->primary_id = screen->GetPrimaryDisplay().id();
+  std::move(callback).Run(std::move(result));
+}
+
+void ScreenEnumerationImpl::HasMultipleDisplays(
+    HasMultipleDisplaysCallback callback) {
+  auto result = display::Screen::GetScreen()->GetNumDisplays() > 1
+                    ? blink::mojom::MultipleDisplays::kTrue
+                    : blink::mojom::MultipleDisplays::kFalse;
+  std::move(callback).Run(result);
 }
 
 }  // namespace content
\ No newline at end of file
diff --git a/content/browser/screen_enumeration/screen_enumeration_impl.h b/content/browser/screen_enumeration/screen_enumeration_impl.h
index 442efe6..3434c56 100644
--- a/content/browser/screen_enumeration/screen_enumeration_impl.h
+++ b/content/browser/screen_enumeration/screen_enumeration_impl.h
@@ -28,6 +28,7 @@
 
   // blink::mojom::ScreenEnumeration:
   void GetDisplays(GetDisplaysCallback callback) override;
+  void HasMultipleDisplays(HasMultipleDisplaysCallback callback) override;
 
  private:
   // Called with the result of the permission request in GetDisplays().
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index 532fc43..497c17ff 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -5,6 +5,7 @@
 #include "content/browser/site_instance_impl.h"
 
 #include <string>
+#include <tuple>
 
 #include "base/command_line.h"
 #include "base/debug/crash_logging.h"
@@ -63,13 +64,22 @@
 SiteInfo::SiteInfo(const GURL& site_url, const GURL& process_lock_url)
     : site_url_(site_url), process_lock_url_(process_lock_url) {}
 
+// static
+auto SiteInfo::MakeTie(const SiteInfo& site_info) {
+  return std::tie(site_info.site_url_.possibly_invalid_spec(),
+                  site_info.process_lock_url_.possibly_invalid_spec());
+}
+
 bool SiteInfo::operator==(const SiteInfo& other) const {
-  return site_url_ == other.site_url_ &&
-         process_lock_url_ == other.process_lock_url_;
+  return MakeTie(*this) == MakeTie(other);
 }
 
 bool SiteInfo::operator!=(const SiteInfo& other) const {
-  return !(*this == other);
+  return MakeTie(*this) != MakeTie(other);
+}
+
+bool SiteInfo::operator<(const SiteInfo& other) const {
+  return MakeTie(*this) < MakeTie(other);
 }
 
 std::string SiteInfo::GetDebugString() const {
@@ -483,8 +493,8 @@
   return has_site_;
 }
 
-bool SiteInstanceImpl::HasRelatedSiteInstance(const GURL& url) {
-  return browsing_instance_->HasSiteInstance(url);
+bool SiteInstanceImpl::HasRelatedSiteInstance(const SiteInfo& site_info) {
+  return browsing_instance_->HasSiteInstance(site_info);
 }
 
 scoped_refptr<SiteInstance> SiteInstanceImpl::GetRelatedSiteInstance(
@@ -664,7 +674,8 @@
                GetSiteForURLInternal(GetIsolationContext(), url,
                                      true /* should_use_effective_urls */,
                                      true /* allow_default_site_url */) &&
-           !browsing_instance_->HasSiteInstance(url);
+           !browsing_instance_->HasSiteInstance(
+               ComputeSiteInfo(GetIsolationContext(), url));
   }
 
   return SiteInstanceImpl::IsSameSite(GetIsolationContext(),
diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h
index 48f4ee37..c62fd74 100644
--- a/content/browser/site_instance_impl.h
+++ b/content/browser/site_instance_impl.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_SITE_INSTANCE_IMPL_H_
 #define CONTENT_BROWSER_SITE_INSTANCE_IMPL_H_
 
+#include <functional>
+
 #include <stddef.h>
 #include <stdint.h>
 
@@ -87,13 +89,21 @@
   //                if the SiteInstance's process isn't going to be locked.
   const GURL& process_lock_url() const { return process_lock_url_; }
 
+  // Returns false if the site_url() is empty.
+  bool is_empty() const { return site_url().possibly_invalid_spec().empty(); }
+
   bool operator==(const SiteInfo& other) const;
   bool operator!=(const SiteInfo& other) const;
 
+  // Defined to allow this object to act as a key for std::map and std::set.
+  bool operator<(const SiteInfo& other) const;
+
   // Returns a string representation of this SiteInfo principal.
   std::string GetDebugString() const;
 
  private:
+  static auto MakeTie(const SiteInfo& site_info);
+
   GURL site_url_;
   // The URL to use when locking a process to this SiteInstance's site via
   // SetProcessLock(). This is the same as |site_url_| except for cases
@@ -305,9 +315,9 @@
   bool HasSite() const;
 
   // Returns whether there is currently a related SiteInstance (registered with
-  // BrowsingInstance) for the site of the given url.  If so, we should try to
-  // avoid dedicating an unused SiteInstance to it (e.g., in a new tab).
-  bool HasRelatedSiteInstance(const GURL& url);
+  // BrowsingInstance) for the given SiteInfo.  If so, we should try to avoid
+  // dedicating an unused SiteInstance to it (e.g., in a new tab).
+  bool HasRelatedSiteInstance(const SiteInfo& site_info);
 
   // Returns whether this SiteInstance is compatible with and can host the given
   // |url|. If not, the browser should force a SiteInstance swap when
@@ -427,7 +437,7 @@
   // Returns true if this object was constructed as a default site instance.
   bool IsDefaultSiteInstance() const;
 
-  // Returns true if |site_url| is a site URL that the BrowsingInstance has
+  // Returns true if |site_url| is a site url that the BrowsingInstance has
   // associated with its default SiteInstance.
   bool IsSiteInDefaultSiteInstance(const GURL& site_url) const;
 
diff --git a/content/browser/site_instance_impl_unittest.cc b/content/browser/site_instance_impl_unittest.cc
index 09f43b1..dcaf6af3 100644
--- a/content/browser/site_instance_impl_unittest.cc
+++ b/content/browser/site_instance_impl_unittest.cc
@@ -171,6 +171,11 @@
 
   BrowserContext* context() { return &context_; }
 
+  SiteInfo GetSiteInfoForURL(const std::string& url) {
+    return SiteInstanceImpl::ComputeSiteInfo(IsolationContext(&context_),
+                                             GURL(url));
+  }
+
  private:
   BrowserTaskEnvironment task_environment_;
   TestBrowserContext context_;
@@ -631,17 +636,17 @@
 
   // Should be able to see that we do have SiteInstances.
   EXPECT_TRUE(browsing_instance->HasSiteInstance(
-      GURL("http://mail.google.com")));
+      GetSiteInfoForURL("http://mail.google.com")));
   EXPECT_TRUE(browsing_instance2->HasSiteInstance(
-      GURL("http://mail.google.com")));
+      GetSiteInfoForURL("http://mail.google.com")));
   EXPECT_TRUE(browsing_instance->HasSiteInstance(
-      GURL("http://mail.yahoo.com")));
+      GetSiteInfoForURL("http://mail.yahoo.com")));
 
   // Should be able to see that we don't have SiteInstances.
   EXPECT_FALSE(browsing_instance->HasSiteInstance(
-      GURL("https://www.google.com")));
+      GetSiteInfoForURL("https://www.google.com")));
   EXPECT_FALSE(browsing_instance2->HasSiteInstance(
-      GURL("http://www.yahoo.com")));
+      GetSiteInfoForURL("http://www.yahoo.com")));
 
   // browsing_instances will be deleted when their SiteInstances are deleted.
   // The processes will be unregistered when the RPH scoped_ptrs go away.
@@ -709,19 +714,19 @@
 
   // Should be able to see that we do have SiteInstances.
   EXPECT_TRUE(browsing_instance->HasSiteInstance(
-      GURL("http://mail.google.com")));  // visited before
+      GetSiteInfoForURL("http://mail.google.com")));  // visited before
   EXPECT_TRUE(browsing_instance2->HasSiteInstance(
-      GURL("http://mail.google.com")));  // visited before
+      GetSiteInfoForURL("http://mail.google.com")));  // visited before
   EXPECT_TRUE(browsing_instance->HasSiteInstance(
-      GURL("http://mail.yahoo.com")));  // visited before
+      GetSiteInfoForURL("http://mail.yahoo.com")));  // visited before
 
   // Should be able to see that we don't have SiteInstances.
-  EXPECT_FALSE(browsing_instance2->HasSiteInstance(
-      GURL("http://www.yahoo.com")));  // different BI, same browser context
+  EXPECT_FALSE(browsing_instance2->HasSiteInstance(GetSiteInfoForURL(
+      "http://www.yahoo.com")));  // different BI, same browser context
   EXPECT_FALSE(browsing_instance->HasSiteInstance(
-      GURL("https://www.google.com")));  // not visited before
-  EXPECT_FALSE(browsing_instance3->HasSiteInstance(
-      GURL("http://www.yahoo.com")));  // different BI, different context
+      GetSiteInfoForURL("https://www.google.com")));  // not visited before
+  EXPECT_FALSE(browsing_instance3->HasSiteInstance(GetSiteInfoForURL(
+      "http://www.yahoo.com")));  // different BI, different context
 
   // browsing_instances will be deleted when their SiteInstances are deleted.
   // The processes will be unregistered when the RPH scoped_ptrs go away.
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc
index 3003369..7175315 100644
--- a/content/browser/webauth/authenticator_common.cc
+++ b/content/browser/webauth/authenticator_common.cc
@@ -534,15 +534,11 @@
 
 }  // namespace
 
-AuthenticatorCommon::AuthenticatorCommon(
-    RenderFrameHost* render_frame_host,
-    std::unique_ptr<base::OneShotTimer> timer)
+AuthenticatorCommon::AuthenticatorCommon(RenderFrameHost* render_frame_host)
     : render_frame_host_(render_frame_host),
       security_checker_(static_cast<RenderFrameHostImpl*>(render_frame_host)
-                            ->GetWebAuthRequestSecurityChecker()),
-      timer_(std::move(timer)) {
+                            ->GetWebAuthRequestSecurityChecker()) {
   DCHECK(render_frame_host_);
-  DCHECK(timer_);
   // Disable the back-forward cache for any document that makes WebAuthn
   // requests. Pages using privacy-sensitive APIs are generally exempt from
   // back-forward cache for now as a precaution.
diff --git a/content/browser/webauth/authenticator_common.h b/content/browser/webauth/authenticator_common.h
index b928e18..adc7630 100644
--- a/content/browser/webauth/authenticator_common.h
+++ b/content/browser/webauth/authenticator_common.h
@@ -16,6 +16,7 @@
 #include "base/containers/span.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "base/timer/timer.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/authenticator_request_client_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -67,9 +68,7 @@
 // Common code for any WebAuthn Authenticator interfaces.
 class CONTENT_EXPORT AuthenticatorCommon {
  public:
-  // Permits setting timer for testing.
-  AuthenticatorCommon(RenderFrameHost* render_frame_host,
-                      std::unique_ptr<base::OneShotTimer>);
+  explicit AuthenticatorCommon(RenderFrameHost* render_frame_host);
   virtual ~AuthenticatorCommon();
 
   // This is not-quite an implementation of blink::mojom::Authenticator. The
@@ -183,7 +182,8 @@
   url::Origin caller_origin_;
   std::string relying_party_id_;
   scoped_refptr<WebAuthRequestSecurityChecker> security_checker_;
-  std::unique_ptr<base::OneShotTimer> timer_;
+  std::unique_ptr<base::OneShotTimer> timer_ =
+      std::make_unique<base::OneShotTimer>();
   base::Optional<device::AuthenticatorSelectionCriteria>
       authenticator_selection_criteria_;
   base::Optional<std::string> app_id_;
diff --git a/content/browser/webauth/authenticator_impl.cc b/content/browser/webauth/authenticator_impl.cc
index 504870a..ff68183 100644
--- a/content/browser/webauth/authenticator_impl.cc
+++ b/content/browser/webauth/authenticator_impl.cc
@@ -17,10 +17,9 @@
 namespace content {
 
 AuthenticatorImpl::AuthenticatorImpl(RenderFrameHost* render_frame_host)
-    : AuthenticatorImpl(render_frame_host,
-                        std::make_unique<AuthenticatorCommon>(
-                            render_frame_host,
-                            std::make_unique<base::OneShotTimer>())) {}
+    : AuthenticatorImpl(
+          render_frame_host,
+          std::make_unique<AuthenticatorCommon>(render_frame_host)) {}
 
 AuthenticatorImpl::AuthenticatorImpl(
     RenderFrameHost* render_frame_host,
diff --git a/content/browser/webauth/authenticator_impl.h b/content/browser/webauth/authenticator_impl.h
index ffc0537..f3f6f60 100644
--- a/content/browser/webauth/authenticator_impl.h
+++ b/content/browser/webauth/authenticator_impl.h
@@ -46,9 +46,8 @@
  public:
   explicit AuthenticatorImpl(RenderFrameHost* render_frame_host);
 
-  // By being able to set AuthenticatorCommon, this constructor permits setting
-  // the timer for testing. Using this constructor will also empty out the
-  // protocol set, since no device discovery will take place during tests.
+  // Constructs an AuthenticatorImpl with an injected AuthenticatorCommon for
+  // testing.
   AuthenticatorImpl(RenderFrameHost* render_frame_host,
                     std::unique_ptr<AuthenticatorCommon> authenticator_common);
   ~AuthenticatorImpl() override;
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 3a03cfa..10879d02 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -109,12 +109,7 @@
 using FailureReasonCallbackReceiver =
     ::device::test::TestCallbackReceiver<InterestingFailureReason>;
 
-typedef struct {
-  const char* origin;
-  // Either a relying party ID or a U2F AppID.
-  const char* claimed_authority;
-  AuthenticatorStatus expected_status;
-} OriginClaimedAuthorityPair;
+constexpr base::TimeDelta kTestTimeout = base::TimeDelta::FromMinutes(1);
 
 // The size of credential IDs returned by GetTestCredentials().
 constexpr size_t kTestCredentialIdLength = 32u;
@@ -140,6 +135,13 @@
     R"({"challenge":"aHE0loIi7BcgLkJQX47SsWriLxa7BbiMJdueYCZF8UE","origin":)"
     R"("https://a.google.com", "type":"webauthn.get"})";
 
+typedef struct {
+  const char* origin;
+  // Either a relying party ID or a U2F AppID.
+  const char* claimed_authority;
+  AuthenticatorStatus expected_status;
+} OriginClaimedAuthorityPair;
+
 constexpr OriginClaimedAuthorityPair kValidRelyingPartyTestCases[] = {
     {"http://localhost", "localhost", AuthenticatorStatus::SUCCESS},
     {"https://myawesomedomain", "myawesomedomain",
@@ -394,7 +396,9 @@
 
 class AuthenticatorTestBase : public content::RenderViewHostTestHarness {
  protected:
-  AuthenticatorTestBase() = default;
+  AuthenticatorTestBase()
+      : RenderViewHostTestHarness(
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
   ~AuthenticatorTestBase() override = default;
 
   void SetUp() override {
@@ -453,25 +457,6 @@
     return authenticator;
   }
 
-  mojo::Remote<blink::mojom::Authenticator> ConnectToAuthenticator(
-      std::unique_ptr<base::OneShotTimer> timer) {
-    authenticator_impl_ = std::make_unique<AuthenticatorImpl>(
-        main_rfh(),
-        std::make_unique<AuthenticatorCommon>(main_rfh(), std::move(timer)));
-    mojo::Remote<blink::mojom::Authenticator> authenticator;
-    authenticator_impl_->Bind(authenticator.BindNewPipeAndPassReceiver());
-    return authenticator;
-  }
-
-  mojo::Remote<blink::mojom::Authenticator> ConstructAuthenticatorWithTimer(
-      scoped_refptr<base::TestMockTimeTaskRunner> task_runner) {
-    // Set up a timer for testing.
-    auto timer =
-        std::make_unique<base::OneShotTimer>(task_runner->GetMockTickClock());
-    timer->SetTaskRunner(task_runner);
-    return ConnectToAuthenticator(std::move(timer));
-  }
-
   url::Origin GetTestOrigin() {
     const GURL test_relying_party_url(kTestOrigin1);
     CHECK(test_relying_party_url.is_valid());
@@ -693,9 +678,7 @@
 // verification is required for U2F devices.
 TEST_F(AuthenticatorImplTest, MakeCredentialUserVerification) {
   SimulateNavigation(GURL(kTestOrigin1));
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
 
   PublicKeyCredentialCreationOptionsPtr options =
       GetTestPublicKeyCredentialCreationOptions();
@@ -705,9 +688,7 @@
   TestMakeCredentialCallback callback_receiver;
   authenticator->MakeCredential(std::move(options),
                                 callback_receiver.callback());
-  // Trigger timer.
-  base::RunLoop().RunUntilIdle();
-  task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(kTestTimeout);
   callback_receiver.WaitForCallback();
   EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status());
 }
@@ -716,9 +697,7 @@
 // key is requested on create().
 TEST_F(AuthenticatorImplTest, MakeCredentialResidentKey) {
   SimulateNavigation(GURL(kTestOrigin1));
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
 
   PublicKeyCredentialCreationOptionsPtr options =
       GetTestPublicKeyCredentialCreationOptions();
@@ -727,9 +706,7 @@
   TestMakeCredentialCallback callback_receiver;
   authenticator->MakeCredential(std::move(options),
                                 callback_receiver.callback());
-  // Trigger timer.
-  base::RunLoop().RunUntilIdle();
-  task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(kTestTimeout);
   callback_receiver.WaitForCallback();
   EXPECT_EQ(AuthenticatorStatus::RESIDENT_CREDENTIALS_UNSUPPORTED,
             callback_receiver.status());
@@ -741,9 +718,7 @@
 // platform authenticator is requested for U2F devices.
 TEST_F(AuthenticatorImplTest, MakeCredentialPlatformAuthenticator) {
   SimulateNavigation(GURL(kTestOrigin1));
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
 
   PublicKeyCredentialCreationOptionsPtr options =
       GetTestPublicKeyCredentialCreationOptions();
@@ -753,9 +728,7 @@
   TestMakeCredentialCallback callback_receiver;
   authenticator->MakeCredential(std::move(options),
                                 callback_receiver.callback());
-  // Trigger timer.
-  base::RunLoop().RunUntilIdle();
-  task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(kTestTimeout);
   callback_receiver.WaitForCallback();
   EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status());
 }
@@ -811,15 +784,11 @@
       GetTestPublicKeyCredentialCreationOptions();
   TestMakeCredentialCallback callback_receiver;
 
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
   authenticator->MakeCredential(std::move(options),
                                 callback_receiver.callback());
 
-  // Trigger timer.
-  base::RunLoop().RunUntilIdle();
-  task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(kTestTimeout);
   callback_receiver.WaitForCallback();
   EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status());
 }
@@ -911,14 +880,8 @@
 
 // Verify that a request coming from Cryptotoken bypasses origin checks.
 TEST_F(AuthenticatorImplTest, CryptotokenBypass) {
-  SimulateNavigation(GURL(kTestOrigin1));
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
-
   {
-    OverrideLastCommittedOrigin(main_rfh(),
-                                url::Origin::Create(GURL(kCryptotokenOrigin)));
+    SimulateNavigation(GURL(kCryptotokenOrigin));
     // First, verify that the Cryptotoken request succeeds with the appid.
     PublicKeyCredentialRequestOptionsPtr options =
         GetTestPublicKeyCredentialRequestOptions();
@@ -931,6 +894,7 @@
     options->appid = kTestOrigin1;
 
     TestGetAssertionCallback callback_receiver;
+    auto authenticator = ConnectToAuthenticator();
     authenticator->GetAssertion(std::move(options),
                                 callback_receiver.callback());
     callback_receiver.WaitForCallback();
@@ -942,8 +906,7 @@
 
   {
     ResetVirtualDevice();
-    OverrideLastCommittedOrigin(
-        main_rfh(), url::Origin::Create(GURL(kTestExtensionOrigin)));
+    SimulateNavigation(GURL(kTestExtensionOrigin));
     // Next, verify that other extensions cannot bypass the origin checks.
     PublicKeyCredentialRequestOptionsPtr options =
         GetTestPublicKeyCredentialRequestOptions();
@@ -956,6 +919,7 @@
     options->appid = kTestOrigin1;
 
     TestGetAssertionCallback callback_receiver;
+    auto authenticator = ConnectToAuthenticator();
     authenticator->GetAssertion(std::move(options),
                                 callback_receiver.callback());
     callback_receiver.WaitForCallback();
@@ -989,9 +953,7 @@
       device::ProtocolVersion::kCtap2);
 
   SimulateNavigation(GURL(kCryptotokenOrigin));
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
   PublicKeyCredentialCreationOptionsPtr options =
       GetTestPublicKeyCredentialCreationOptions();
   options->relying_party.id = kTestOrigin1;
@@ -999,8 +961,7 @@
   authenticator->MakeCredential(std::move(options),
                                 callback_receiver.callback());
 
-  base::RunLoop().RunUntilIdle();
-  task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(kTestTimeout);
   callback_receiver.WaitForCallback();
 
   EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status());
@@ -1016,9 +977,7 @@
   virtual_device_factory_->SetCtap2Config(config);
 
   SimulateNavigation(GURL(kCryptotokenOrigin));
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
   PublicKeyCredentialCreationOptionsPtr options =
       GetTestPublicKeyCredentialCreationOptions();
   options->relying_party.id = kTestOrigin1;
@@ -1070,13 +1029,10 @@
   options->appid = kTestOrigin1;
 
   TestGetAssertionCallback callback_receiver;
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
   authenticator->GetAssertion(std::move(options), callback_receiver.callback());
 
-  base::RunLoop().RunUntilIdle();
-  task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(kTestTimeout);
   callback_receiver.WaitForCallback();
 
   EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status());
@@ -1110,48 +1066,38 @@
 // Test that Cryptotoken requests should only be dispatched to USB
 // authenticators.
 TEST_F(AuthenticatorImplTest, CryptotokenUsbOnly) {
-  SimulateNavigation(GURL(kTestOrigin1));
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  SimulateNavigation(GURL(kCryptotokenOrigin));
 
-  for (const bool is_cryptotoken_request : {false, true}) {
-    // caBLE and platform discoveries cannot be instantiated through
-    // VirtualFidoDeviceFactory, so we don't test them here.
-    for (const device::FidoTransportProtocol transport :
-         {device::FidoTransportProtocol::kUsbHumanInterfaceDevice,
-          device::FidoTransportProtocol::kBluetoothLowEnergy,
-          device::FidoTransportProtocol::kNearFieldCommunication}) {
-      SCOPED_TRACE(::testing::Message()
-                   << "is_cryptotoken_request=" << is_cryptotoken_request
-                   << ", transport=" << device::ToString(transport));
+  // caBLE and platform discoveries cannot be instantiated through
+  // VirtualFidoDeviceFactory, so we don't test them here.
+  for (const device::FidoTransportProtocol transport :
+       {device::FidoTransportProtocol::kUsbHumanInterfaceDevice,
+        device::FidoTransportProtocol::kBluetoothLowEnergy,
+        device::FidoTransportProtocol::kNearFieldCommunication}) {
+    SCOPED_TRACE(::testing::Message()
+                 << "transport=" << device::ToString(transport));
 
-      OverrideLastCommittedOrigin(
-          main_rfh(),
-          url::Origin::Create(GURL(is_cryptotoken_request ? kCryptotokenOrigin
-                                                          : kTestOrigin1)));
+    ResetVirtualDevice();
+    virtual_device_factory_->SetSupportedProtocol(
+        device::ProtocolVersion::kU2f);
+    virtual_device_factory_->SetTransport(transport);
+    virtual_device_factory_->mutable_state()->transport = transport;
 
-      ResetVirtualDevice();
-      virtual_device_factory_->SetSupportedProtocol(
-          device::ProtocolVersion::kU2f);
-      virtual_device_factory_->SetTransport(transport);
-      virtual_device_factory_->mutable_state()->transport = transport;
+    PublicKeyCredentialCreationOptionsPtr options =
+        GetTestPublicKeyCredentialCreationOptions();
+    auto authenticator = ConnectToAuthenticator();
+    TestMakeCredentialCallback callback_receiver;
+    authenticator->MakeCredential(std::move(options),
+                                  callback_receiver.callback());
 
-      PublicKeyCredentialCreationOptionsPtr options =
-          GetTestPublicKeyCredentialCreationOptions();
-      TestMakeCredentialCallback callback_receiver;
-      authenticator->MakeCredential(std::move(options),
-                                    callback_receiver.callback());
-      base::RunLoop().RunUntilIdle();
-      task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+    if (transport == device::FidoTransportProtocol::kUsbHumanInterfaceDevice) {
       callback_receiver.WaitForCallback();
-      EXPECT_EQ(
-          !is_cryptotoken_request ||
-                  transport ==
-                      device::FidoTransportProtocol::kUsbHumanInterfaceDevice
-              ? AuthenticatorStatus::SUCCESS
-              : AuthenticatorStatus::NOT_ALLOWED_ERROR,
-          callback_receiver.status());
+      EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status());
+    } else {
+      task_environment()->FastForwardBy(kTestTimeout);
+      callback_receiver.WaitForCallback();
+      EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR,
+                callback_receiver.status());
     }
   }
 }
@@ -1159,9 +1105,7 @@
 // Verify that a credential registered with U2F can be used via webauthn.
 TEST_F(AuthenticatorImplTest, AppIdExtension) {
   SimulateNavigation(GURL(kTestOrigin1));
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
 
   {
     // First, test that the appid extension isn't echoed at all when not
@@ -1348,14 +1292,10 @@
       GetTestPublicKeyCredentialRequestOptions();
   TestGetAssertionCallback callback_receiver;
 
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
   authenticator->GetAssertion(std::move(options), callback_receiver.callback());
 
-  // Trigger timer.
-  base::RunLoop().RunUntilIdle();
-  task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(kTestTimeout);
   callback_receiver.WaitForCallback();
   EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status());
 }
@@ -1456,9 +1396,7 @@
   PublicKeyCredentialRequestOptionsPtr options =
       GetTestPublicKeyCredentialRequestOptions();
   TestGetAssertionCallback callback_receiver;
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
   // Inject credential ID to the virtual device so that successful sign in is
   // possible.
   ASSERT_TRUE(virtual_device_factory_->mutable_state()->InjectRegistration(
@@ -1466,7 +1404,6 @@
 
   authenticator->GetAssertion(std::move(options), callback_receiver.callback());
 
-  // Trigger timer.
   callback_receiver.WaitForCallback();
   EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status());
 }
@@ -1478,14 +1415,10 @@
   options->allow_credentials.clear();
   TestGetAssertionCallback callback_receiver;
 
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
   authenticator->GetAssertion(std::move(options), callback_receiver.callback());
 
-  // Trigger timer.
-  base::RunLoop().RunUntilIdle();
-  task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(kTestTimeout);
   callback_receiver.WaitForCallback();
   EXPECT_EQ(AuthenticatorStatus::RESIDENT_CREDENTIALS_UNSUPPORTED,
             callback_receiver.status());
@@ -1595,9 +1528,7 @@
   virtual_device_factory_->mutable_state()->simulate_invalid_response = true;
   SimulateNavigation(GURL(kTestOrigin1));
 
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
 
   {
     PublicKeyCredentialRequestOptionsPtr options =
@@ -1605,9 +1536,7 @@
     TestGetAssertionCallback callback_receiver;
     authenticator->GetAssertion(std::move(options),
                                 callback_receiver.callback());
-    // Trigger timer.
-    base::RunLoop().RunUntilIdle();
-    task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+    task_environment()->FastForwardBy(kTestTimeout);
     callback_receiver.WaitForCallback();
     EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR,
               callback_receiver.status());
@@ -1619,9 +1548,7 @@
     TestMakeCredentialCallback callback_receiver;
     authenticator->MakeCredential(std::move(options),
                                   callback_receiver.callback());
-    // Trigger timer.
-    base::RunLoop().RunUntilIdle();
-    task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+    task_environment()->FastForwardBy(kTestTimeout);
     callback_receiver.WaitForCallback();
     EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR,
               callback_receiver.status());
@@ -1674,15 +1601,12 @@
       options->allow_credentials[0].id(), kTestRelyingPartyId));
 
   SimulateNavigation(GURL(kTestOrigin1));
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
   TestGetAssertionCallback callback_receiver;
 
   authenticator->GetAssertion(std::move(options), callback_receiver.callback());
 
-  base::RunLoop().RunUntilIdle();
-  task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(kTestTimeout);
   callback_receiver.WaitForCallback();
   EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status());
 }
@@ -2686,12 +2610,7 @@
 
 TEST_F(AuthenticatorContentBrowserClientTest,
        CryptotokenBypassesAttestationConsentPrompt) {
-  SimulateNavigation(GURL(kTestOrigin1));
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
-  OverrideLastCommittedOrigin(main_rfh(),
-                              url::Origin::Create(GURL(kCryptotokenOrigin)));
+  SimulateNavigation(GURL(kCryptotokenOrigin));
 
   virtual_device_factory_->SetSupportedProtocol(device::ProtocolVersion::kU2f);
   PublicKeyCredentialCreationOptionsPtr options =
@@ -2702,6 +2621,7 @@
   options->attestation = device::AttestationConveyancePreference::kDirect;
   test_client_.attestation_consent = AttestationConsent::DENIED;
 
+  auto authenticator = ConnectToAuthenticator();
   TestMakeCredentialCallback callback_receiver;
   authenticator->MakeCredential(std::move(options),
                                 callback_receiver.callback());
@@ -2788,9 +2708,8 @@
  public:
   explicit FakeAuthenticatorCommon(
       RenderFrameHost* render_frame_host,
-      std::unique_ptr<base::OneShotTimer> timer,
       std::unique_ptr<MockAuthenticatorRequestDelegateObserver> mock_delegate)
-      : AuthenticatorCommon(render_frame_host, std::move(timer)),
+      : AuthenticatorCommon(render_frame_host),
         mock_delegate_(std::move(mock_delegate)) {}
   ~FakeAuthenticatorCommon() override = default;
 
@@ -2818,25 +2737,14 @@
   }
 
   mojo::Remote<blink::mojom::Authenticator> ConnectToFakeAuthenticator(
-      std::unique_ptr<MockAuthenticatorRequestDelegateObserver> delegate,
-      std::unique_ptr<base::OneShotTimer> timer) {
+      std::unique_ptr<MockAuthenticatorRequestDelegateObserver> delegate) {
     authenticator_impl_ = std::make_unique<AuthenticatorImpl>(
         main_rfh(), std::make_unique<FakeAuthenticatorCommon>(
-                        main_rfh(), std::move(timer), std::move(delegate)));
+                        main_rfh(), std::move(delegate)));
     mojo::Remote<blink::mojom::Authenticator> authenticator;
     authenticator_impl_->Bind(authenticator.BindNewPipeAndPassReceiver());
     return authenticator;
   }
-
-  mojo::Remote<blink::mojom::Authenticator> ConstructFakeAuthenticatorWithTimer(
-      std::unique_ptr<MockAuthenticatorRequestDelegateObserver> delegate,
-      scoped_refptr<base::TestMockTimeTaskRunner> task_runner) {
-    // Set up a timer for testing.
-    auto timer =
-        std::make_unique<base::OneShotTimer>(task_runner->GetMockTickClock());
-    timer->SetTaskRunner(task_runner);
-    return ConnectToFakeAuthenticator(std::move(delegate), std::move(timer));
-  }
 };
 
 TEST_F(AuthenticatorImplRequestDelegateTest,
@@ -2854,14 +2762,11 @@
   PublicKeyCredentialRequestOptionsPtr options =
       GetTestPublicKeyCredentialRequestOptions();
   TestGetAssertionCallback callback_receiver;
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
 
   auto mock_delegate =
       std::make_unique<MockAuthenticatorRequestDelegateObserver>();
   auto* const mock_delegate_ptr = mock_delegate.get();
-  auto authenticator = ConstructFakeAuthenticatorWithTimer(
-      std::move(mock_delegate), task_runner);
+  auto authenticator = ConnectToFakeAuthenticator(std::move(mock_delegate));
 
   auto mock_usb_device = device::MockFidoDevice::MakeCtap();
   mock_usb_device->StubGetId();
@@ -2905,17 +2810,13 @@
   auto mock_delegate = std::make_unique<
       ::testing::NiceMock<MockAuthenticatorRequestDelegateObserver>>(
       failure_reason_receiver.callback());
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructFakeAuthenticatorWithTimer(
-      std::move(mock_delegate), task_runner);
+  auto authenticator = ConnectToFakeAuthenticator(std::move(mock_delegate));
 
   TestGetAssertionCallback callback_receiver;
   authenticator->GetAssertion(GetTestPublicKeyCredentialRequestOptions(),
                               callback_receiver.callback());
 
-  base::RunLoop().RunUntilIdle();
-  task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(kTestTimeout);
 
   callback_receiver.WaitForCallback();
   EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status());
@@ -2934,10 +2835,7 @@
   auto mock_delegate = std::make_unique<
       ::testing::NiceMock<MockAuthenticatorRequestDelegateObserver>>(
       failure_reason_receiver.callback());
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructFakeAuthenticatorWithTimer(
-      std::move(mock_delegate), task_runner);
+  auto authenticator = ConnectToFakeAuthenticator(std::move(mock_delegate));
 
   PublicKeyCredentialCreationOptionsPtr options =
       GetTestPublicKeyCredentialCreationOptions();
@@ -2967,10 +2865,7 @@
   auto mock_delegate = std::make_unique<
       ::testing::NiceMock<MockAuthenticatorRequestDelegateObserver>>(
       failure_reason_receiver.callback());
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructFakeAuthenticatorWithTimer(
-      std::move(mock_delegate), task_runner);
+  auto authenticator = ConnectToFakeAuthenticator(std::move(mock_delegate));
 
   TestGetAssertionCallback callback_receiver;
   authenticator->GetAssertion(GetTestPublicKeyCredentialRequestOptions(),
@@ -3098,7 +2993,6 @@
         ConnectToAuthenticator();
     authenticator->MakeCredential(std::move(options),
                                   callback_receiver.callback());
-    base::RunLoop().RunUntilIdle();
     callback_receiver.WaitForCallback();
     EXPECT_EQ(callback_receiver.status(),
               has_excluded_credential ? AuthenticatorStatus::CREDENTIAL_EXCLUDED
@@ -3135,7 +3029,6 @@
     TestGetAssertionCallback callback_receiver;
     authenticator->GetAssertion(std::move(options),
                                 callback_receiver.callback());
-    base::RunLoop().RunUntilIdle();
     callback_receiver.WaitForCallback();
     EXPECT_EQ(callback_receiver.status(),
               has_allowed_credential ? AuthenticatorStatus::SUCCESS
@@ -3175,7 +3068,6 @@
     TestGetAssertionCallback callback_receiver;
     authenticator->GetAssertion(std::move(options),
                                 callback_receiver.callback());
-    base::RunLoop().RunUntilIdle();
     callback_receiver.WaitForCallback();
 
     EXPECT_EQ(callback_receiver.status(), AuthenticatorStatus::SUCCESS);
@@ -3215,7 +3107,6 @@
     TestGetAssertionCallback callback_receiver;
     authenticator->GetAssertion(std::move(options),
                                 callback_receiver.callback());
-    base::RunLoop().RunUntilIdle();
     callback_receiver.WaitForCallback();
 
     EXPECT_EQ(callback_receiver.status(),
@@ -3230,10 +3121,7 @@
   // whether an authenticator returns credential information when the allowlist
   // only has a single entry.
   NavigateAndCommit(GURL(kTestOrigin1));
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  mojo::Remote<blink::mojom::Authenticator> authenticator =
-      ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
 
   for (const auto behavior :
        {device::VirtualCtap2Device::Config::IncludeCredential::ONLY_IF_NEEDED,
@@ -3283,9 +3171,8 @@
     TestGetAssertionCallback callback_receiver;
     authenticator->GetAssertion(std::move(options),
                                 callback_receiver.callback());
-    base::RunLoop().RunUntilIdle();
     if (should_timeout) {
-      task_runner->FastForwardBy(base::TimeDelta::FromMinutes(5));
+      task_environment()->FastForwardBy(kTestTimeout);
     }
     callback_receiver.WaitForCallback();
 
@@ -3357,7 +3244,6 @@
   TestMakeCredentialCallback create_callback;
   authenticator->MakeCredential(GetTestPublicKeyCredentialCreationOptions(),
                                 create_callback.callback());
-  base::RunLoop().RunUntilIdle();
   create_callback.WaitForCallback();
   EXPECT_EQ(create_callback.status(), AuthenticatorStatus::NOT_ALLOWED_ERROR);
 
@@ -3369,7 +3255,6 @@
   TestGetAssertionCallback assertion_callback;
   authenticator->GetAssertion(std::move(assertion_options),
                               assertion_callback.callback());
-  base::RunLoop().RunUntilIdle();
   assertion_callback.WaitForCallback();
   EXPECT_EQ(assertion_callback.status(),
             AuthenticatorStatus::NOT_ALLOWED_ERROR);
@@ -3389,7 +3274,6 @@
   TestMakeCredentialCallback create_callback;
   authenticator->MakeCredential(GetTestPublicKeyCredentialCreationOptions(),
                                 create_callback.callback());
-  base::RunLoop().RunUntilIdle();
   create_callback.WaitForCallback();
   EXPECT_EQ(create_callback.status(), AuthenticatorStatus::SUCCESS);
 
@@ -3401,7 +3285,6 @@
   TestGetAssertionCallback assertion_callback;
   authenticator->GetAssertion(std::move(assertion_options),
                               assertion_callback.callback());
-  base::RunLoop().RunUntilIdle();
   assertion_callback.WaitForCallback();
   EXPECT_EQ(assertion_callback.status(), AuthenticatorStatus::SUCCESS);
 }
@@ -3420,7 +3303,6 @@
   TestMakeCredentialCallback create_callback;
   authenticator->MakeCredential(GetTestPublicKeyCredentialCreationOptions(),
                                 create_callback.callback());
-  base::RunLoop().RunUntilIdle();
   create_callback.WaitForCallback();
   EXPECT_EQ(create_callback.status(), AuthenticatorStatus::SUCCESS);
 
@@ -3431,7 +3313,6 @@
   TestGetAssertionCallback assertion_callback;
   authenticator->GetAssertion(std::move(assertion_options),
                               assertion_callback.callback());
-  base::RunLoop().RunUntilIdle();
   assertion_callback.WaitForCallback();
   EXPECT_EQ(assertion_callback.status(), AuthenticatorStatus::SUCCESS);
 }
@@ -3451,7 +3332,6 @@
   TestMakeCredentialCallback create_callback;
   authenticator->MakeCredential(GetTestPublicKeyCredentialCreationOptions(),
                                 create_callback.callback());
-  base::RunLoop().RunUntilIdle();
   create_callback.WaitForCallback();
   EXPECT_EQ(create_callback.status(), AuthenticatorStatus::NOT_ALLOWED_ERROR);
 
@@ -3463,7 +3343,6 @@
   TestGetAssertionCallback assertion_callback;
   authenticator->GetAssertion(std::move(assertion_options),
                               assertion_callback.callback());
-  base::RunLoop().RunUntilIdle();
   assertion_callback.WaitForCallback();
   EXPECT_EQ(assertion_callback.status(),
             AuthenticatorStatus::NOT_ALLOWED_ERROR);
@@ -3507,7 +3386,6 @@
     options->exclude_credentials = std::move(test_credentials);
     TestMakeCredentialCallback callback;
     authenticator->MakeCredential(std::move(options), callback.callback());
-    base::RunLoop().RunUntilIdle();
     callback.WaitForCallback();
 
     EXPECT_EQ(callback.status(), authenticator_has_excluded_credential
@@ -3542,7 +3420,6 @@
         GetTestPublicKeyCredentialParameters(static_cast<int32_t>(test.algo));
     TestMakeCredentialCallback callback;
     authenticator->MakeCredential(std::move(options), callback.callback());
-    base::RunLoop().RunUntilIdle();
     callback.WaitForCallback();
 
     ASSERT_EQ(callback.status(), AuthenticatorStatus::SUCCESS);
@@ -3572,29 +3449,24 @@
   // This is a regression test for crbug.com/1087158.
   NavigateAndCommit(GURL(kTestOrigin1));
 
-  base::RunLoop run_loop;
+  // Make the entire discovery factory disappear mid-request.
+  bool was_called = false;
   virtual_device_factory_->SetSupportedProtocol(
       device::ProtocolVersion::kCtap2);
   virtual_device_factory_->mutable_state()->simulate_press_callback =
       base::BindLambdaForTesting([&](device::VirtualFidoDevice* device) {
-        run_loop.QuitClosure().Run();
+        was_called = true;
+        ResetVirtualDevice();
         return false;
       });
 
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  mojo::Remote<blink::mojom::Authenticator> authenticator =
-      ConstructAuthenticatorWithTimer(task_runner);
+  auto authenticator = ConnectToAuthenticator();
   PublicKeyCredentialCreationOptionsPtr options =
       GetTestPublicKeyCredentialCreationOptions();
   TestMakeCredentialCallback callback;
   authenticator->MakeCredential(std::move(options), callback.callback());
 
-  // Reset the FidoDiscoveryFactory while the request is processing, then let it
-  // time out.
-  run_loop.Run();
-  ResetVirtualDevice();
-  task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(kTestTimeout);
 
   callback.WaitForCallback();
   EXPECT_EQ(callback.status(), AuthenticatorStatus::NOT_ALLOWED_ERROR);
@@ -4396,11 +4268,8 @@
 }
 
 TEST_F(InternalUVAuthenticatorImplTest, MakeCredentialCryptotoken) {
-  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-      base::Time::Now(), base::TimeTicks::Now());
-  auto authenticator = ConstructAuthenticatorWithTimer(task_runner);
-  OverrideLastCommittedOrigin(main_rfh(),
-                              url::Origin::Create(GURL(kCryptotokenOrigin)));
+  SimulateNavigation(GURL(kCryptotokenOrigin));
+  auto authenticator = ConnectToAuthenticator();
 
   for (const auto fingerprints_enrolled : {false, true}) {
     SCOPED_TRACE(::testing::Message()
@@ -4558,10 +4427,7 @@
 }
 
 TEST_F(InternalUVAuthenticatorImplTest, GetAssertionCryptotoken) {
-  mojo::Remote<blink::mojom::Authenticator> authenticator =
-      ConnectToAuthenticator();
-  OverrideLastCommittedOrigin(main_rfh(),
-                              url::Origin::Create(GURL(kCryptotokenOrigin)));
+  SimulateNavigation(GURL(kCryptotokenOrigin));
   ASSERT_TRUE(virtual_device_factory_->mutable_state()->InjectRegistration(
       get_credential_options()->allow_credentials[0].id(),
       kTestRelyingPartyId));
@@ -4571,6 +4437,7 @@
                  << "fingerprints_enrolled=" << fingerprints_enrolled);
     virtual_device_factory_->mutable_state()->fingerprints_enrolled =
         fingerprints_enrolled;
+    auto authenticator = ConnectToAuthenticator();
     TestGetAssertionCallback callback_receiver;
     authenticator->GetAssertion(
         get_credential_options(device::UserVerificationRequirement::kPreferred),
@@ -5783,7 +5650,7 @@
   void TearDown() override {
     // The |RenderFrameHost| must outlive |AuthenticatorImpl|.
     internal_authenticator_impl_.reset();
-    content::RenderViewHostTestHarness::TearDown();
+    AuthenticatorTestBase::TearDown();
   }
 
   void NavigateAndCommit(const GURL& url) {
@@ -5800,26 +5667,6 @@
     return internal_authenticator_impl_.get();
   }
 
-  InternalAuthenticatorImpl* ConnectToAuthenticator(
-      const url::Origin& effective_origin_url,
-      std::unique_ptr<base::OneShotTimer> timer) {
-    internal_authenticator_impl_.reset(new InternalAuthenticatorImpl(
-        main_rfh(),
-        std::make_unique<AuthenticatorCommon>(main_rfh(), std::move(timer))));
-    internal_authenticator_impl_->SetEffectiveOrigin(effective_origin_url);
-    return internal_authenticator_impl_.get();
-  }
-
-  InternalAuthenticatorImpl* ConstructAuthenticatorWithTimer(
-      const url::Origin& effective_origin_url,
-      scoped_refptr<base::TestMockTimeTaskRunner> task_runner) {
-    // Set up a timer for testing.
-    auto timer =
-        std::make_unique<base::OneShotTimer>(task_runner->GetMockTickClock());
-    timer->SetTaskRunner(task_runner);
-    return ConnectToAuthenticator(effective_origin_url, std::move(timer));
-  }
-
  protected:
   std::unique_ptr<InternalAuthenticatorImpl> internal_authenticator_impl_;
 };
@@ -5858,10 +5705,8 @@
                  std::string(test_case.origin));
 
     NavigateAndCommit(GURL("https://this.isthewrong.origin"));
-    auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-        base::Time::Now(), base::TimeTicks::Now());
-    auto* authenticator = ConstructAuthenticatorWithTimer(
-        url::Origin::Create(GURL(test_case.origin)), task_runner);
+    auto* authenticator =
+        GetAuthenticator(url::Origin::Create(GURL(test_case.origin)));
     PublicKeyCredentialCreationOptionsPtr options =
         GetTestPublicKeyCredentialCreationOptions();
     options->relying_party.id = test_case.claimed_authority;
@@ -5912,10 +5757,8 @@
                  std::string(test_case.origin));
 
     NavigateAndCommit(GURL("https://this.isthewrong.origin"));
-    auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
-        base::Time::Now(), base::TimeTicks::Now());
-    auto* authenticator = ConstructAuthenticatorWithTimer(
-        url::Origin::Create(GURL(test_case.origin)), task_runner);
+    InternalAuthenticatorImpl* authenticator =
+        GetAuthenticator(url::Origin::Create(GURL(test_case.origin)));
     PublicKeyCredentialRequestOptionsPtr options =
         GetTestPublicKeyCredentialRequestOptions();
     options->relying_party_id = test_case.claimed_authority;
diff --git a/content/common/service_worker/service_worker_utils.cc b/content/common/service_worker/service_worker_utils.cc
index 2bd3586..11d45678 100644
--- a/content/common/service_worker/service_worker_utils.cc
+++ b/content/common/service_worker/service_worker_utils.cc
@@ -13,6 +13,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/origin_util.h"
+#include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_byte_range.h"
 #include "net/http/http_util.h"
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/GestureListenerManagerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/GestureListenerManagerTest.java
index 3fc8ba42..22c88ed 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/GestureListenerManagerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/GestureListenerManagerTest.java
@@ -21,6 +21,7 @@
 import org.chromium.content_public.browser.GestureStateListenerWithScroll;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_public.browser.test.RenderFrameHostTestExt;
 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TouchCommon;
@@ -90,10 +91,25 @@
         // This needs to wait for first-paint, otherwise scrolling doesn't happen.
         TestCallbackHelperContainer callbackHelperContainer =
                 new TestCallbackHelperContainer(webContents);
-        CallbackHelper done = callbackHelperContainer.getOnFirstVisuallyNonEmptyPaintHelper();
         mActivityTestRule.loadUrl(webContents.getNavigationController(), callbackHelperContainer,
                 new LoadUrlParams(TEST_URL));
-        done.waitForCallback(0);
+        // Wait for both a paint and the page to finish loading. These may come in any order.
+        callbackHelperContainer.getOnFirstVisuallyNonEmptyPaintHelper().waitForCallback(0);
+        callbackHelperContainer.getOnPageFinishedHelper().waitForCallback(0);
+
+        // At this point the page has finished loading and a non-empty paint occurred. This does not
+        // mean the renderer is fully ready to process events (processing events requires layers,
+        // which may not have been created yet). Wait for a visual update, which should ensure the
+        // renderer is ready.
+        CallbackHelper callbackHelper = new CallbackHelper();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            new RenderFrameHostTestExt(webContents.getMainFrame())
+                    .updateVisualState((Boolean result) -> {
+                        Assert.assertTrue(result);
+                        callbackHelper.notifyCalled();
+                    });
+        });
+        callbackHelper.waitForFirst();
 
         final GestureStateListenerImpl listener = new GestureStateListenerImpl();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
diff --git a/content/public/common/referrer.cc b/content/public/common/referrer.cc
index 2faa0972..7da9325 100644
--- a/content/public/common/referrer.cc
+++ b/content/public/common/referrer.cc
@@ -7,6 +7,7 @@
 #include <atomic>
 #include <string>
 
+#include "base/command_line.h"
 #include "base/numerics/safe_conversions.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/RenderFrameHostTestExt.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/RenderFrameHostTestExt.java
index df94955..9534ed8 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/RenderFrameHostTestExt.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/RenderFrameHostTestExt.java
@@ -31,7 +31,13 @@
         nativeExecuteJavaScript(mNativeRenderFrameHostTestExt, script, callback);
     }
 
+    public void updateVisualState(Callback<Boolean> callback) {
+        nativeUpdateVisualState(mNativeRenderFrameHostTestExt, callback);
+    }
+
     private native long nativeInit(long renderFrameHostAndroidPtr);
     private native void nativeExecuteJavaScript(
             long nativeRenderFrameHostTestExt, String script, Callback<String> callback);
+    private native void nativeUpdateVisualState(
+            long nativeRenderFrameHostTestExt, Callback<Boolean> callback);
 }
diff --git a/content/public/test/android/render_frame_host_test_ext.cc b/content/public/test/android/render_frame_host_test_ext.cc
index 55ea8a48..728be9e4 100644
--- a/content/public/test/android/render_frame_host_test_ext.cc
+++ b/content/public/test/android/render_frame_host_test_ext.cc
@@ -61,4 +61,14 @@
   render_frame_host_->ExecuteJavaScriptForTests(script, std::move(callback));
 }
 
+void RenderFrameHostTestExt::UpdateVisualState(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj,
+    const JavaParamRef<jobject>& jcallback) {
+  auto result_callback = base::BindOnce(
+      &base::android::RunBooleanCallbackAndroid,
+      base::android::ScopedJavaGlobalRef<jobject>(env, jcallback));
+  render_frame_host_->InsertVisualStateCallback(std::move(result_callback));
+}
+
 }  // namespace content
diff --git a/content/public/test/android/render_frame_host_test_ext.h b/content/public/test/android/render_frame_host_test_ext.h
index ab58bc38..66fa8117 100644
--- a/content/public/test/android/render_frame_host_test_ext.h
+++ b/content/public/test/android/render_frame_host_test_ext.h
@@ -22,6 +22,11 @@
                          const base::android::JavaParamRef<jobject>& obj,
                          const base::android::JavaParamRef<jstring>& jscript,
                          const base::android::JavaParamRef<jobject>& jcallback);
+  // This calls InsertVisualStateCallback(). See it for details on the return
+  // value.
+  void UpdateVisualState(JNIEnv* env,
+                         const base::android::JavaParamRef<jobject>& obj,
+                         const base::android::JavaParamRef<jobject>& jcallback);
 
  private:
   RenderFrameHostImpl* const render_frame_host_;
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 5f5d5a4..b2c1bcd69 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -450,6 +450,7 @@
       "//media/fuchsia/audio",
       "//media/fuchsia/cdm/client",
       "//media/fuchsia/mojom",
+      "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
     ]
   }
 
diff --git a/content/renderer/media/fuchsia_renderer_factory.cc b/content/renderer/media/fuchsia_renderer_factory.cc
index 30af09d0..097fc23 100644
--- a/content/renderer/media/fuchsia_renderer_factory.cc
+++ b/content/renderer/media/fuchsia_renderer_factory.cc
@@ -4,6 +4,7 @@
 
 #include "content/renderer/media/fuchsia_renderer_factory.h"
 
+#include <fuchsia/media/cpp/fidl.h>
 #include <memory>
 #include <utility>
 
@@ -14,6 +15,9 @@
 #include "media/renderers/video_renderer_impl.h"
 #include "media/video/gpu_memory_buffer_video_frame_pool.h"
 #include "media/video/gpu_video_accelerator_factories.h"
+#include "media/fuchsia/mojom/fuchsia_media_resource_provider.mojom.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 
 namespace content {
@@ -57,8 +61,14 @@
   interface_broker_->GetInterface(
       media_resource_provider.InitWithNewPipeAndPassReceiver());
 
+  mojo::Remote<media::mojom::FuchsiaMediaResourceProvider>
+      remote_media_resource_provider;
+  remote_media_resource_provider.Bind(std::move(media_resource_provider));
+  fidl::InterfaceHandle<fuchsia::media::AudioConsumer> audio_consumer_handle;
+  remote_media_resource_provider->CreateAudioConsumer(
+      audio_consumer_handle.NewRequest());
   auto audio_renderer = std::make_unique<media::FuchsiaAudioRenderer>(
-      media_log_, std::move(media_resource_provider));
+      media_log_, std::move(audio_consumer_handle));
 
   media::GpuVideoAcceleratorFactories* gpu_factories = nullptr;
   if (get_gpu_factories_cb_)
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index cd06da3c..433aec7 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -57,7 +57,6 @@
 #include "third_party/blink/public/platform/web_surface_layer_bridge.h"
 #include "third_party/blink/public/platform/web_video_frame_submitter.h"
 #include "third_party/blink/public/web/blink.h"
-#include "third_party/blink/public/web/modules/mediastream/web_media_stream_renderer_factory.h"
 #include "third_party/blink/public/web/modules/mediastream/webmediaplayer_ms.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "url/origin.h"
@@ -709,7 +708,6 @@
 
   return new blink::WebMediaPlayerMS(
       frame, client, GetWebMediaPlayerDelegate(), std::move(media_log),
-      blink::CreateWebMediaStreamRendererFactory(),
       render_frame_->GetTaskRunner(blink::TaskType::kInternalMedia),
       render_thread->GetIOTaskRunner(), video_frame_compositor_task_runner,
       render_thread->GetMediaThreadTaskRunner(),
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index dbf1834..a19c9f1 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -1956,18 +1956,22 @@
 #if BUILDFLAG(ENABLE_PRINTING)
   DCHECK(plugin_print_interface_);
 
-  // |canvas| should always have an associated metafile.
-  auto* metafile = canvas->GetPrintingMetafile();
-  DCHECK(metafile);
+  // When compositing to PDF output, the |canvas| metafile is not the
+  // document metafile, and cannot be used for getting the final PDF.
+  if (current_print_settings_.format != PP_PRINTOUTPUTFORMAT_PDF) {
+    // |canvas| should always have an associated metafile.
+    auto* metafile = canvas->GetPrintingMetafile();
+    DCHECK(metafile);
 
-  // |ranges_| should be empty IFF |metafile_| is not set.
-  DCHECK_EQ(ranges_.empty(), !metafile_);
-  if (metafile_) {
-    // The metafile should be the same across all calls for a given print job.
-    DCHECK_EQ(metafile_, metafile);
-  } else {
-    // Store |metafile| on the first call.
-    metafile_ = metafile;
+    // |ranges_| should be empty IFF |metafile_| is not set.
+    DCHECK_EQ(ranges_.empty(), !metafile_);
+    if (metafile_) {
+      // The metafile should be the same across all calls for a given print job.
+      DCHECK_EQ(metafile_, metafile);
+    } else {
+      // Store |metafile| on the first call.
+      metafile_ = metafile;
+    }
   }
 
   PP_PrintPageNumberRange_Dev page_range = {page_number, page_number};
@@ -1984,8 +1988,7 @@
     PP_Resource print_output = plugin_print_interface_->PrintPages(
         pp_instance(), ranges_.data(), ranges_.size());
     if (print_output) {
-      if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_PDF ||
-          current_print_settings_.format == PP_PRINTOUTPUTFORMAT_RASTER) {
+      if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_RASTER) {
         PrintPDFOutput(print_output, metafile_);
       }
 
diff --git a/content/test/gpu/gpu_tests/context_lost_integration_test.py b/content/test/gpu/gpu_tests/context_lost_integration_test.py
index ed9fd35..a98d911 100644
--- a/content/test/gpu/gpu_tests/context_lost_integration_test.py
+++ b/content/test/gpu/gpu_tests/context_lost_integration_test.py
@@ -362,8 +362,6 @@
     self._RestartBrowser('must restart after tests that kill the GPU process')
 
   def _ContextLost_WorkerRAFAfterGPUCrash(self, test_path):
-    self.RestartBrowserIfNecessaryWithArgs(
-        [cba.ENABLE_EXPERIMENTAL_WEB_PLATFORM_FEATURES])
     self.RestartBrowserIfNecessaryWithArgs([])
     self._NavigateAndWaitForLoad(test_path)
     self._KillGPUProcess(1, False)
@@ -373,7 +371,6 @@
   def _ContextLost_WorkerRAFAfterGPUCrash_OOPD(self, test_path):
     self.RestartBrowserIfNecessaryWithArgs([
         '--enable-viz-display-compositor',
-        cba.ENABLE_EXPERIMENTAL_WEB_PLATFORM_FEATURES,
     ])
     self._NavigateAndWaitForLoad(test_path)
     self._KillGPUProcess(1, False)
diff --git a/docs/security/sheriff.md b/docs/security/sheriff.md
index 9588902..70f0f441 100644
--- a/docs/security/sheriff.md
+++ b/docs/security/sheriff.md
@@ -261,24 +261,39 @@
 mitigated, the V8 team will reduce the security severity (to avoid unnecessary
 risk of merging the bug into stable branches).
 
-#### Step 3. [Label, label, label](security-labels.md).
+#### Step 3. Set Impact
+
+Identify the earliest affected branch (stable, beta or head) and set either
+`Security_Impact-Stable`, `Security_Impact-Beta` or `Security_Impact-Head`.
+If you reproduced the bug with ClusterFuzz, it should do this on your behalf.
+
+#### Step 4. [Check other labels](security-labels.md).
 
 Much of Chrome's development and release process depends on bugs having the
 right labels and components. Labels and components are vitally important for
-our metrics, the visibility of bugs, and tracking our progress over time.
+merging the fix to the right releases, and ensuring reporters are credited
+correctly. They also help with metrics and visibility.
 
-Labels to **double-check** (that should already be there if the bug was filed
-using the Security template):
+Labels to **double-check** (the first two should already be there if the bug
+was filed using the Security template):
 
 * **Restrict-View-SecurityTeam**
 * **Type-Bug-Security**
 * **If the reporter wants to remain anonymous or if the bug description or
   comments contain PII**, add **Restrict-View-SecurityEmbargo**.
+* **Security_Severity** - your responsibility as Sheriff.
+* **Security_Impact** - your responsibility as Sheriff.
 
-Generally, see [the Security Labels document](security-labels.md).
+You can expect Sheriffbot to fill in lots of other labels; for example,
+the `M-` label to indicate the target milestone. It's best to allow
+Sheriffbot to add the rest, as its rules have congealed from years of
+accumulated security wisdom. See
+[the Security Labels document](security-labels.md) for an explanation of what
+the labels mean.
 
-**Ensure the comment adequately explains any status changes.** Severity,
-  milestone, and priority assignment generally require explanatory text.
+**If you change anything, add a comment which explains any status
+changes.** Severity, milestone, and priority assignment generally require
+explanatory text.
 
 * Report suspected malicious URLs to SafeBrowsing:
   * Public URL:
@@ -294,7 +309,7 @@
 ##### Labeling For Chrome On iOS
 
 * Reproduce using iOS device or desktop Safari.
-* Assign severity, impact, milestone, and component labels.
+* Assign severity, impact, and component labels.
 * Label **ExternalDependency**.
 * Label **Hotlist-WebKit**. This label is monitored by Apple friends.
 * File a security bug at [bugs.webkit.org](https://bugs.webkit.org), and CC
diff --git a/docs/testing/web_tests.md b/docs/testing/web_tests.md
index faa428c..3396360 100644
--- a/docs/testing/web_tests.md
+++ b/docs/testing/web_tests.md
@@ -217,7 +217,7 @@
 
 There are two ways to run web tests with additional command-line arguments:
 
-* Using `--additional-driver-flag`:
+* Using `--additional-driver-flag` or `--flag-specific`:
 
   ```bash
   third_party/blink/tools/run_web_tests.py --additional-driver-flag=--blocking-repaint
@@ -300,8 +300,15 @@
   with the flags without creating any virtual tests.
 
 For flags whose implementation is still in progress, virtual test suites and
-flag-specific expectations represent two alternative strategies for testing.
-Consider the following when choosing between them:
+flag-specific expectations represent two alternative strategies for testing both
+the enabled code path and not-enabled code path. They are preferred to only
+setting a [runtime enabled feature](../../third_party/blink/renderer/platform/RuntimeEnabledFeatures.md)
+to `status: "test"` if the feature has substantially different code path from
+production because the latter would cause loss of test coverage of the production
+code path.
+
+Consider the following when choosing between virtual test suites and
+flag-specific expectations:
 
 * The
   [waterfall builders](https://dev.chromium.org/developers/testing/chromium-build-infrastructure/tour-of-the-chromium-buildbot)
@@ -309,7 +316,8 @@
   will run all virtual test suites in addition to the non-virtual tests.
   Conversely, a flag-specific expectations file won't automatically cause the
   bots to test your flag - if you want bot coverage without virtual test suites,
-  you will need to set up a dedicated bot for your flag.
+  you will need to set up a dedicated bot ([example](https://chromium-review.googlesource.com/c/chromium/src/+/1850255))
+  for your flag.
 
 * Due to the above, virtual test suites incur a performance penalty for the
   commit queue and the continuous build infrastructure. This is exacerbated by
diff --git a/docs/ui/create/examples/login_dialog.md b/docs/ui/create/examples/login_dialog.md
new file mode 100644
index 0000000..b415c2d
--- /dev/null
+++ b/docs/ui/create/examples/login_dialog.md
@@ -0,0 +1,554 @@
+# Example: Creating a Simple Login Dialog
+
+[TOC]
+
+## Introduction
+
+The goal of this example is to get you familiar with the basics of the Views
+framework and things to watch out for when developing with Views.
+
+We will be creating the following UI:
+
+![Login dialog UI](login_dialog_ui.png)
+
+
+## Setting up files
+
+To create this simple dialog, you would need to add the following files:
+
+`login_bubble_dialog.h`
+
+``` cpp
+// 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 LOGIN_BUBBLE_DIALOG_H_
+#define LOGIN_BUBBLE_DIALOG_H_
+
+#include "ui/views/bubble/bubble_border.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+
+class LoginBubbleDialogView : public views::BubbleDialogDelegateView {
+ public:
+  static void Show(views::View* anchor_view,
+                   views::BubbleBorder::Arrow anchor_position);
+
+  ~LoginBubbleDialogView() override;
+
+ private:
+  LoginBubbleDialogView(views::View* anchor_view,
+                        views::BubbleBorder::Arrow anchor_position);
+};
+
+#endif  // LOGIN_BUBBLE_DIALOG_H_
+```
+
+
+`login_bubble_dialog.cc`
+
+``` cpp
+// 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 "login_bubble_dialog.h"
+
+#include "ui/views/border.h"
+
+// static
+void LoginBubbleDialogView::Show(views::View* anchor_view,
+                                 views::BubbleBorder::Arrow anchor_position) {
+  BubbleDialogDelegateView::CreateBubble(
+      new LoginBubbleDialogView(anchor_view, anchor_position))
+      ->Show();
+}
+
+LoginBubbleDialogView::~LoginBubbleDialogView() = default;
+
+LoginBubbleDialogView::LoginBubbleDialogView(
+    views::View* anchor_view,
+    views::BubbleBorder::Arrow anchor_position)
+    : BubbleDialogDelegateView(anchor_view, anchor_position) {}
+```
+
+Make sure to add these files to the relevant build file of your project.
+
+Calling `Show()` will create a simple dialog with OK and Cancel buttons and a
+window close button. These UI elements are provided by default by the
+`BubbleDialogDelegateView`.
+
+
+## Creating the layout
+
+To populate the login dialog, we first create a [`LayoutManager`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/layout/layout_manager.h) in the
+dialog’s constructor.
+
+
+`login_bubble_dialog.cc`
+
+
+``` cpp
+LoginBubbleDialogView::LoginBubbleDialogView(
+    views::View* anchor_view,
+    views::BubbleBorder::Arrow anchor_position)
+    : BubbleDialogDelegateView(anchor_view, anchor_position) {
+  ...
+ const LayoutProvider* provider = LayoutProvider::Get();
+  set_margins(
+      provider->GetDialogInsetsForContentType(views::CONTROL, views::CONTROL));
+  const int related_control_padding =
+      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL);
+  const int label_padding =
+      provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL);
+
+  GridLayout* layout = SetLayoutManager(std::make_unique<GridLayout>());
+  ColumnSet* column_set = layout->AddColumnSet(0);
+  column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL,
+                        GridLayout::kFixedSize, GridLayout::USE_PREF, 0, 0);
+  column_set->AddPaddingColumn(0, label_padding);
+  column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0,
+                        GridLayout::USE_PREF, 0, 0);
+}
+```
+
+
+This creates a 2x2 [`GridLayout`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/layout/grid_layout.h)
+with horizontal padding specified by the layout constant
+`DISTANCE_RELATED_LABEL_HORIZONTAL`. The first column will hold the form’s
+[`Label`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/controls/label.h)s and the second will hold the
+[`Textfield`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/controls/textfield/textfield.h)s for user input.
+
+To help with adding rows to the `GridLayout`, add the following scoped helper
+and relevant headers.
+
+
+`login_bubble_dialog.cc`
+
+``` cpp
+#include "base/strings/string16.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/layout_provider.h"
+...
+namespace {
+
+// Adds a label textfield pair to the login dialog's layout.
+views::Textfield* AddFormRow(views::GridLayout* layout,
+                             const base::string16& label_text) {
+  layout->StartRow(0, 0);
+  views::Label* label =
+      layout->AddView(std::make_unique<views::Label>(label_text));
+  views::Textfield* textfield =
+      layout->AddView(std::make_unique<views::Textfield>());
+  textfield->SetAssociatedLabel(label);
+  constexpr int kDefaultTextfieldWidth = 30;
+  constexpr int kMinimumTextfieldWidth = 5;
+  textfield->SetDefaultWidthInChars(kDefaultTextfieldWidth);
+  textfield->SetMinimumWidthInChars(kMinimumTextfieldWidth);
+  return textfield;
+}
+
+}  // namespace
+...
+```
+
+This creates a new [`GridLayout`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/layout/grid_layout.h)
+row for a given field in our form. The call to
+[`SetAssociatedLabel()`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/controls/textfield/textfield.h;l=250;drc=291180454e079aa5c3677dc3f3eaf619a1cf1d42)
+sets the accessible label relationship between the
+[`Label`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/controls/label.h) and the [`Textfield`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/controls/textfield/textfield.h)
+and copies the `Label`’s accessible name to the `Textfield`.
+
+
+## Dealing with localized text
+
+Next we need to create localized text constants for the bubble dialog title,
+button label, username and password.
+[UI Localization document](https://www.chromium.org/developers/design-documents/ui-localization)
+will guide you on how to create these string constants.
+
+Create the following string constants:
+
+* `IDS_LOGIN_DIALOG_TITLE`
+* `IDS_LOGIN_DIALOG_OK_BUTTON_LABEL`
+* `IDS_LOGIN_DIALOG_USERNAME_ACCESSIBLE_NAME`
+* `IDS_LOGIN_DIALOG_PASSWORD_ACCESSIBLE_NAME`
+
+Add the following to the bubble dialog header and implementation files.
+
+
+`login_bubble_dialog.h`
+
+
+``` cpp
+#include "ui/views/controls/textfield/textfield.h"
+...
+class LoginBubbleDialogView : public views::BubbleDialogDelegateView {
+  ...
+ private:
+  ...
+  views::Textfield* username_ = nullptr;
+  views::Textfield* password_ = nullptr;
+};
+```
+
+
+`login_bubble_dialog.cc`
+
+
+``` cpp
+#include "ui/base/l10n/l10n_util.h"
+...
+LoginBubbleDialogView::LoginBubbleDialogView(
+    views::View* anchor_view,
+    views::BubbleBorder::Arrow anchor_position)
+    : BubbleDialogDelegateView(anchor_view, anchor_position) {
+  ...
+  SetTitle(l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_TITLE));
+  SetButtonLabel(ui::DIALOG_BUTTON_OK, l10n_util::GetStringUTF16(
+      IDS_STARTER_DIALOG_OK_BUTTON_LABEL));
+  ...
+  username_ = AddFormRow(layout, l10n_util::GetStringUTF16(
+      IDS_LOGIN_DIALOG_USERNAME_ACCESSIBLE_NAME));
+
+  layout->AddPaddingRow(0, related_control_padding);
+
+  password_ = AddFormRow(layout, l10n_util::GetStringUTF16(
+      IDS_LOGIN_DIALOG_PASSWORD_ACCESSIBLE_NAME));
+  password_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
+}
+```
+
+
+This adds the username and password fields to the form and sets the username
+with default focus.
+
+
+## Handling user input
+
+To respond to a click on the login button, we define a callback that gets
+passed in by the caller. Update the code as follows.
+
+
+`login_bubble_dialog.h`
+
+
+``` cpp
+#include "base/strings/string16.h"
+...
+class LoginBubbleDialogView : public views::BubbleDialogDelegateView {
+ public:
+  using OnSubmitCallback = base::OnceCallback<void(base::string16 username,
+                                                   base::string16 password)>;
+
+  static void Show(View* anchor_view,
+                   BubbleBorder::Arrow anchor_position,
+                   OnSubmitCallback accept_callback);
+  ...
+
+ private:
+ LoginBubbleDialogView(View* anchor_view,
+                        BubbleBorder::Arrow anchor_position,
+                        OnSubmitCallback accept_callback);
+...
+}
+```
+
+
+`login_bubble_dialog.cc`
+
+
+``` cpp
+#include "base/bind.h"
+#include "base/callback_forward.h"
+...
+// static
+void LoginBubbleDialogView::Show(View* anchor_view,
+                                 BubbleBorder::Arrow anchor_position,
+                                 OnSubmitCallback accept_callback) {
+  BubbleDialogDelegateView::CreateBubble(
+      new LoginBubbleDialogView(anchor_view, anchor_position,
+                                std::move(accept_callback)))
+      ->Show();
+}
+...
+
+LoginBubbleDialogView::LoginBubbleDialogView(
+    View* anchor_view,
+    BubbleBorder::Arrow anchor_position,
+    OnSubmitCallback accept_callback)
+    : BubbleDialogDelegateView(anchor_view, anchor_position) {
+  ...
+  const auto on_submit = [](const LoginBubbleDialogView* bubble_view,
+                            OnSubmitCallback accept_callback) {
+    std::move(accept_callback)
+        .Run(bubble_view->username_->GetText(),
+             bubble_view->password_->GetText());
+  };
+  SetAcceptCallback(base::BindOnce(on_submit, base::Unretained(this),
+                                   std::move(accept_callback)));
+  ...
+}
+```
+
+Now the dialog will call the `accept_callback` after the login button has been
+clicked before the dialog closes.
+
+
+## Form validation & dialog updates
+
+Next we want to disable the bubble dialog’s login button until both the
+username and password fields have been populated.
+
+We achieve this by having the `LoginBubbleDialogView` inherit from
+[`TextfieldController`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/controls/textfield/textfield_controller.h),
+and on each [`Textfield`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/controls/textfield/textfield.h)
+update it checks to make sure that both fields are populated before enabling
+the login button.
+
+Modify the code as follows:
+
+
+`login_bubble_dialog.h`
+
+
+``` cpp
+#include "ui/views/controls/textfield/textfield_controller.h"
+...
+class LoginBubbleDialogView : public views::BubbleDialogDelegateView,
+                              public views::TextfieldController {
+ public:
+  ...
+  // TextfieldController:
+  void ContentsChanged(Textfield* sender,
+                       const base::string16& new_contents) override;
+  ...
+};
+```
+
+
+`login_bubble_dialog.cc`
+
+
+``` cpp
+views::Textfield* AddFormRow(LoginBubbleDialogView* bubble,
+                             views::GridLayout* layout,
+                             const base::string16& label_text) {
+  ...
+  textfield->set_controller(bubble);
+  return textfield;
+}
+...
+
+void LoginBubbleDialogView::ContentsChanged(
+    views::Textfield* sender,
+    const base::string16& new_contents) {
+  SetButtonEnabled(ui::DIALOG_BUTTON_OK, !username_->GetText().empty() &&
+                                             !password_->GetText().empty());
+  DialogModelChanged();
+}
+
+LoginBubbleDialogView::LoginBubbleDialogView(
+    views::View* anchor_view,
+    views::BubbleBorder::Arrow anchor_position,
+    LoginBubbleDialogController* controller)
+    : BubbleDialogDelegateView(anchor_view, anchor_position),
+      controller_(controller) {
+  SetButtonEnabled(ui::DIALOG_BUTTON_OK, false);
+  ...
+  username_ = AddFormRow(
+      this, layout,
+      l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_USERNAME_ACCESSIBLE_NAME));
+
+  layout->AddPaddingRow(0, related_control_padding);
+
+  password_ = AddFormRow(
+      this, layout,
+      l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_PASSWORD_ACCESSIBLE_NAME));
+  password_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
+}
+```
+
+
+## Result
+
+The final code should resemble the following:
+
+
+`login_bubble_dialog.h`
+
+
+```
+// 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 LOGIN_BUBBLE_DIALOG_H_
+#define LOGIN_BUBBLE_DIALOG_H_
+
+#include "base/strings/string16.h"
+#include "ui/views/bubble/bubble_border.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
+
+class LoginBubbleDialogView : public BubbleDialogDelegateView,
+                              public TextfieldController {
+ public:
+  using OnSubmitCallback = base::OnceCallback<void(base::string16 username,
+                                                   base::string16 password)>;
+
+  static void Show(View* anchor_view,
+                   BubbleBorder::Arrow anchor_position,
+                   OnSubmitCallback accept_callback);
+
+  ~LoginBubbleDialogView() override;
+
+  // TextfieldController:
+  void ContentsChanged(Textfield* sender,
+                       const base::string16& new_contents) override;
+
+ private:
+  LoginBubbleDialogView(View* anchor_view,
+                        BubbleBorder::Arrow anchor_position,
+                        OnSubmitCallback accept_callback);
+
+  Textfield* username_ = nullptr;
+  Textfield* password_ = nullptr;
+};
+
+#endif  // LOGIN_BUBBLE_DIALOG_H_
+```
+
+
+`login_bubble_dialog.cc`
+
+
+```
+// 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 "login_bubble_dialog.h"
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/strings/string16.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/layout_provider.h"
+
+namespace {
+
+// Adds a label textfield pair to the login dialog's layout.
+Textfield* AddFormRow(LoginBubbleDialogView* bubble,
+                      GridLayout* layout,
+                      const base::string16& label_text) {
+  layout->StartRow(0, 0);
+  Label* label = layout->AddView(std::make_unique<Label>(label_text));
+  Textfield* textfield = layout->AddView(std::make_unique<Textfield>());
+  textfield->SetAssociatedLabel(label);
+  textfield->set_controller(bubble);
+  constexpr int kDefaultTextfieldWidth = 30;
+  constexpr int kMinimumTextfieldWidth = 5;
+  textfield->SetDefaultWidthInChars(kDefaultTextfieldWidth);
+  textfield->SetMinimumWidthInChars(kMinimumTextfieldWidth);
+  return textfield;
+}
+
+}  // namespace
+
+// static
+void LoginBubbleDialogView::Show(View* anchor_view,
+                                 BubbleBorder::Arrow anchor_position,
+                                 OnSubmitCallback accept_callback) {
+  BubbleDialogDelegateView::CreateBubble(
+      new LoginBubbleDialogView(anchor_view, anchor_position,
+                                std::move(accept_callback)))
+      ->Show();
+}
+
+LoginBubbleDialogView::~LoginBubbleDialogView() = default;
+
+void LoginBubbleDialogView::ContentsChanged(
+    Textfield* sender,
+    const base::string16& new_contents) {
+  SetButtonEnabled(ui::DIALOG_BUTTON_OK, !username_->GetText().empty() &&
+                                             !password_->GetText().empty());
+  DialogModelChanged();
+}
+
+LoginBubbleDialogView::LoginBubbleDialogView(
+    View* anchor_view,
+    BubbleBorder::Arrow anchor_position,
+    OnSubmitCallback accept_callback)
+    : BubbleDialogDelegateView(anchor_view, anchor_position) {
+  SetButtonEnabled(ui::DIALOG_BUTTON_OK, false);
+
+  const auto on_submit = [](const LoginBubbleDialogView* bubble_view,
+                            OnSubmitCallback accept_callback) {
+    std::move(accept_callback)
+        .Run(bubble_view->username_->GetText(),
+             bubble_view->password_->GetText());
+  };
+  SetAcceptCallback(base::BindOnce(on_submit, base::Unretained(this),
+                                   std::move(accept_callback)));
+
+  SetTitle(l10n_util::GetStringUTF16(IDS_EXAMPLE_LOGIN_DIALOG_TITLE));
+  SetButtonLabel(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(IDS_EXAMPLE_LOGIN_DIALOG_OK_BUTTON_LABEL));
+
+  const LayoutProvider* provider = LayoutProvider::Get();
+  set_margins(
+      provider->GetDialogInsetsForContentType(views::CONTROL, views::CONTROL));
+  const int related_control_padding =
+      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL);
+  const int label_padding =
+      provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL);
+  GridLayout* layout = SetLayoutManager(std::make_unique<GridLayout>());
+  ColumnSet* column_set = layout->AddColumnSet(0);
+  column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL,
+                        GridLayout::kFixedSize, GridLayout::USE_PREF, 0, 0);
+  column_set->AddPaddingColumn(0, label_padding);
+  column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0,
+                        GridLayout::USE_PREF, 0, 0);
+
+  username_ =
+      AddFormRow(this, layout,
+                 l10n_util::GetStringUTF16(
+                     IDS_EXAMPLE_LOGIN_DIALOG_USERNAME_ACCESSIBLE_NAME));
+
+  layout->AddPaddingRow(0, related_control_padding);
+
+  password_ =
+      AddFormRow(this, layout,
+                 l10n_util::GetStringUTF16(
+                     IDS_EXAMPLE_LOGIN_DIALOG_PASSWORD_ACCESSIBLE_NAME));
+  password_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
+}
+
+}  // namespace examples
+}  // namespace views
+```
+
+The generated UI would be:
+
+![Login dialog UI](login_dialog_ui.png)
+
+
+## Run the Example
+
+The example code can be run inside Views example app. You can find the code
+inside the Views example at
+[`ui/views/examples/login_bubble_dialog.cc`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/examples/login_bubble_dialog.cc).
+You can try out this example by running the following command:
+
+``` shell
+$ autoninja -C out\Default views_examples
+$ out\Default\views_examples --enable-examples="Login Bubble Dialog"
+```
+
diff --git a/docs/ui/create/examples/login_dialog_ui.png b/docs/ui/create/examples/login_dialog_ui.png
new file mode 100644
index 0000000..e9620d6c
--- /dev/null
+++ b/docs/ui/create/examples/login_dialog_ui.png
Binary files differ
diff --git a/docs/ui/create/examples/theme_aware.md b/docs/ui/create/examples/theme_aware.md
index 97d18c5..4a8733c 100644
--- a/docs/ui/create/examples/theme_aware.md
+++ b/docs/ui/create/examples/theme_aware.md
@@ -35,7 +35,7 @@
 such as how to lay out UI elements and how to customize them.
 
 
-## Run the Example
+## Run the example
 
 The example code is in the file
 [`ui/views/examples/colored_dialog_example.cc`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/examples/colored_dialog_example.cc)
@@ -50,7 +50,7 @@
 ```
 
 
-## Technique 1: Use Existing Views Controls
+## Technique 1: use existing Views controls
 
 The example's dialog consists of a title, a text field, and two buttons.
 For all these components, you can use Views’ existing controls. The existing
@@ -83,7 +83,7 @@
 ```
 
 
-## Technique 2: Override `OnThemeChanged()` in Custom Controls
+## Technique 2: override `OnThemeChanged()` in custom controls
 
 The checkbox in the main UI overrides `OnThemeChanged()` to implement
 customized behavior (in this case, automatically adjusting its visible state
@@ -129,7 +129,7 @@
 the colors are calculated) may not even work correctly to begin with.
 
 
-## Technique 3: Use Theme Neutral Icons and Images
+## Technique 3: use theme neutral icons and images
 
 The button in the main UI contains an icon. Using a vector icon (as shown in
 the example) makes it easy to re-rasterize to the correct color any time the
@@ -173,7 +173,7 @@
 ```
 
 
-## Learn More
+## Learn more
 
 To experiment with all Views examples and controls, run the examples app
 without any argument, which will show a list of all the examples and controls
diff --git a/docs/ui/create/index.md b/docs/ui/create/index.md
index d2f56ca..8c900d5 100644
--- a/docs/ui/create/index.md
+++ b/docs/ui/create/index.md
@@ -2,4 +2,5 @@
 
 # Examples
 
+* [Example: Creating a Simple Login Dialog](examples/login_dialog.md)
 * [Example: Theme-Aware UI](examples/theme_aware.md)
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 988d3f9..0d6fb4b0 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -749,7 +749,6 @@
   if (is_chromeos) {
     sources += [
       "api/audio/audio_device_id_calculator_unittest.cc",
-      "api/document_scan/document_scan_api_unittest.cc",
       "api/feedback_private/access_rate_limiter_chromeos_unittest.cc",
       "api/feedback_private/feedback_private_api_chromeos_unittest.cc",
       "api/feedback_private/feedback_private_api_unittest_base_chromeos.cc",
@@ -767,7 +766,6 @@
 
     deps += [
       "//chromeos:test_support",
-      "//chromeos/dbus:lorgnette_proto",
       "//chromeos/dbus:test_support",
       "//chromeos/dbus/audio",
       "//chromeos/dbus/media_analytics",
diff --git a/extensions/browser/api/BUILD.gn b/extensions/browser/api/BUILD.gn
index 6b0d44a..cd771e5d 100644
--- a/extensions/browser/api/BUILD.gn
+++ b/extensions/browser/api/BUILD.gn
@@ -132,7 +132,6 @@
       "//extensions/browser/api/clipboard",
       "//extensions/browser/api/crash_report_private",
       "//extensions/browser/api/diagnostics",
-      "//extensions/browser/api/document_scan",
       "//extensions/browser/api/networking_config",
       "//extensions/browser/api/system_power_source",
       "//extensions/browser/api/virtual_keyboard",
diff --git a/extensions/browser/api/declarative/rules_cache_delegate.cc b/extensions/browser/api/declarative/rules_cache_delegate.cc
index 662afbe..5a6d2f0 100644
--- a/extensions/browser/api/declarative/rules_cache_delegate.cc
+++ b/extensions/browser/api/declarative/rules_cache_delegate.cc
@@ -16,7 +16,6 @@
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
-#include "extensions/browser/info_map.h"
 #include "extensions/browser/state_store.h"
 #include "extensions/common/permissions/permissions_data.h"
 
diff --git a/extensions/browser/api/device_permissions_manager.cc b/extensions/browser/api/device_permissions_manager.cc
index 1df437f..61997ee6 100644
--- a/extensions/browser/api/device_permissions_manager.cc
+++ b/extensions/browser/api/device_permissions_manager.cc
@@ -22,6 +22,7 @@
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extensions_browser_client.h"
+#include "extensions/common/permissions/api_permission.h"
 #include "extensions/common/value_builder.h"
 #include "extensions/strings/grit/extensions_strings.h"
 #include "services/device/public/cpp/usb/usb_ids.h"
diff --git a/extensions/browser/api/document_scan/BUILD.gn b/extensions/browser/api/document_scan/BUILD.gn
deleted file mode 100644
index 0f3c181..0000000
--- a/extensions/browser/api/document_scan/BUILD.gn
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 2016 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("//extensions/buildflags/buildflags.gni")
-
-assert(enable_extensions,
-       "Cannot depend on extensions because enable_extensions=false.")
-
-assert(is_chromeos)
-
-source_set("document_scan") {
-  sources = [
-    "document_scan_api.cc",
-    "document_scan_api.h",
-  ]
-
-  deps = [
-    "//chromeos/dbus:lorgnette_proto",
-    "//extensions/common/api",
-  ]
-
-  public_deps = [ "//extensions/browser:browser_sources" ]
-}
diff --git a/extensions/browser/api/web_request/permission_helper.cc b/extensions/browser/api/web_request/permission_helper.cc
index 39587f9..426a2c9e26 100644
--- a/extensions/browser/api/web_request/permission_helper.cc
+++ b/extensions/browser/api/web_request/permission_helper.cc
@@ -10,6 +10,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_factory.h"
 #include "extensions/browser/extension_util.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/browser/process_map_factory.h"
 
 namespace extensions {
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 7c5498c..15cec72 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -64,6 +64,7 @@
 #include "extensions/browser/guest_view/guest_view_events.h"
 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/browser/runtime_data.h"
 #include "extensions/browser/warning_service.h"
 #include "extensions/browser/warning_set.h"
diff --git a/extensions/browser/api/web_request/web_request_api_helpers.cc b/extensions/browser/api/web_request/web_request_api_helpers.cc
index 58012fe..42f2fbd9 100644
--- a/extensions/browser/api/web_request/web_request_api_helpers.cc
+++ b/extensions/browser/api/web_request/web_request_api_helpers.cc
@@ -117,6 +117,21 @@
   UMA_HISTOGRAM_ENUMERATION("Extensions.WebRequest.RequestHeaderChanged", type);
 }
 
+void RecordDNRRequestHeaderRemoved(RequestHeaderType type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "Extensions.DeclarativeNetRequest.RequestHeaderRemoved", type);
+}
+
+void RecordDNRRequestHeaderAdded(RequestHeaderType type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "Extensions.DeclarativeNetRequest.RequestHeaderAdded", type);
+}
+
+void RecordDNRRequestHeaderChanged(RequestHeaderType type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "Extensions.DeclarativeNetRequest.RequestHeaderChanged", type);
+}
+
 bool IsStringLowerCaseASCII(base::StringPiece s) {
   return std::none_of(s.begin(), s.end(), base::IsAsciiUpper<char>);
 }
@@ -236,6 +251,21 @@
                             type);
 }
 
+void RecordDNRResponseHeaderChanged(ResponseHeaderType type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "Extensions.DeclarativeNetRequest.ResponseHeaderChanged", type);
+}
+
+void RecordDNRResponseHeaderAdded(ResponseHeaderType type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "Extensions.DeclarativeNetRequest.ResponseHeaderAdded", type);
+}
+
+void RecordDNRResponseHeaderRemoved(ResponseHeaderType type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "Extensions.DeclarativeNetRequest.ResponseHeaderRemoved", type);
+}
+
 using ResponseHeaderEntry = std::pair<const char*, ResponseHeaderType>;
 constexpr ResponseHeaderEntry kResponseHeaderEntries[] = {
     {"accept-patch", ResponseHeaderType::kAcceptPatch},
@@ -389,22 +419,30 @@
     }
     header_actions->emplace(header, header_action);
 
-    // TODO(crbug.com/1088103): Record request headers modified by the
-    // Declarative Net Request API.
     switch (header_info.operation) {
-      case extensions::api::declarative_net_request::HEADER_OPERATION_SET:
+      case extensions::api::declarative_net_request::HEADER_OPERATION_SET: {
+        bool has_header = headers->HasHeader(header);
+
         headers->SetHeader(header, *header_info.value);
         header_modified = true;
         set_headers->insert(header);
+
+        if (has_header)
+          RecordRequestHeader(header, &RecordDNRRequestHeaderChanged);
+        else
+          RecordRequestHeader(header, &RecordDNRRequestHeaderAdded);
         break;
+      }
       case extensions::api::declarative_net_request::HEADER_OPERATION_REMOVE: {
         while (headers->HasHeader(header)) {
           header_modified = true;
           headers->RemoveHeader(header);
         }
 
-        if (header_modified)
+        if (header_modified) {
           removed_headers->insert(header);
+          RecordRequestHeader(header, &RecordDNRRequestHeaderRemoved);
+        }
         break;
       }
       case extensions::api::declarative_net_request::HEADER_OPERATION_APPEND:
@@ -471,14 +509,13 @@
     }
     (*header_actions)[header].push_back(header_action);
 
-    // TODO(crbug.com/1088103): Record response headers modified by the
-    // Declarative Net Request API.
     switch (header_info.operation) {
       case extensions::api::declarative_net_request::HEADER_OPERATION_REMOVE: {
         if (has_header(header)) {
           header_modified = true;
           create_override_headers_if_needed(override_response_headers);
           override_response_headers->get()->RemoveHeader(header);
+          RecordResponseHeader(header, &RecordDNRResponseHeaderRemoved);
         }
 
         break;
@@ -487,6 +524,13 @@
         header_modified = true;
         create_override_headers_if_needed(override_response_headers);
         override_response_headers->get()->AddHeader(header, *header_info.value);
+
+        // Record only the first time a header is appended. appends following a
+        // set from the same extension are treated as part of the set and are
+        // not logged.
+        if ((*header_actions)[header].size() == 1)
+          RecordResponseHeader(header, &RecordDNRResponseHeaderAdded);
+
         break;
       }
       case extensions::api::declarative_net_request::HEADER_OPERATION_SET: {
@@ -494,6 +538,7 @@
         create_override_headers_if_needed(override_response_headers);
         override_response_headers->get()->RemoveHeader(header);
         override_response_headers->get()->AddHeader(header, *header_info.value);
+        RecordResponseHeader(header, &RecordDNRResponseHeaderChanged);
         break;
       }
       case extensions::api::declarative_net_request::HEADER_OPERATION_NONE:
diff --git a/extensions/browser/api/web_request/web_request_permissions.cc b/extensions/browser/api/web_request/web_request_permissions.cc
index 2e670ed..1e7fe35 100644
--- a/extensions/browser/api/web_request/web_request_permissions.cc
+++ b/extensions/browser/api/web_request/web_request_permissions.cc
@@ -19,6 +19,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/extensions_browser_client.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_urls.h"
diff --git a/extensions/browser/api/web_request/web_request_permissions_unittest.cc b/extensions/browser/api/web_request/web_request_permissions_unittest.cc
index 4b5d804..2f07bc82 100644
--- a/extensions/browser/api/web_request/web_request_permissions_unittest.cc
+++ b/extensions/browser/api/web_request/web_request_permissions_unittest.cc
@@ -12,6 +12,7 @@
 #include "extensions/browser/api/web_request/web_request_info.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extensions_test.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/permissions/permission_set.h"
diff --git a/extensions/browser/extension_function.h b/extensions/browser/extension_function.h
index bb6a1ba0..7f83905c 100644
--- a/extensions/browser/extension_function.h
+++ b/extensions/browser/extension_function.h
@@ -21,7 +21,7 @@
 #include "base/timer/elapsed_timer.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/extension_function_histogram_value.h"
-#include "extensions/browser/info_map.h"
+#include "extensions/browser/quota_service.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/features/feature.h"
@@ -43,7 +43,6 @@
 
 namespace extensions {
 class ExtensionFunctionDispatcher;
-class QuotaLimitHeuristic;
 }
 
 #ifdef NDEBUG
diff --git a/extensions/browser/extension_protocols.cc b/extensions/browser/extension_protocols.cc
index 5a7e8ea1..bfae3a8a 100644
--- a/extensions/browser/extension_protocols.cc
+++ b/extensions/browser/extension_protocols.cc
@@ -58,6 +58,7 @@
 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
 #include "extensions/browser/info_map.h"
 #include "extensions/browser/media_router_extension_access_logger.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/browser/url_request_util.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
diff --git a/extensions/browser/extension_service_worker_message_filter.cc b/extensions/browser/extension_service_worker_message_filter.cc
index f2a17ed..8958f549 100644
--- a/extensions/browser/extension_service_worker_message_filter.cc
+++ b/extensions/browser/extension_service_worker_message_filter.cc
@@ -12,6 +12,7 @@
 #include "extensions/browser/events/event_ack_data.h"
 #include "extensions/browser/extension_function_dispatcher.h"
 #include "extensions/browser/process_manager.h"
+#include "extensions/browser/process_map.h"
 #include "extensions/browser/service_worker_task_queue.h"
 #include "extensions/common/extension_messages.h"
 
diff --git a/extensions/browser/info_map.cc b/extensions/browser/info_map.cc
index f32cd58..dd1838dc 100644
--- a/extensions/browser/info_map.cc
+++ b/extensions/browser/info_map.cc
@@ -5,16 +5,10 @@
 #include "extensions/browser/info_map.h"
 
 #include "base/strings/string_util.h"
-#include "content/public/browser/browser_thread.h"
 #include "extensions/browser/content_verifier.h"
 #include "extensions/browser/unloaded_extension_reason.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/extension_resource.h"
-#include "extensions/common/extension_set.h"
-#include "extensions/common/manifest_handlers/incognito_info.h"
-#include "extensions/common/permissions/permissions_data.h"
-#include "url/gurl.h"
 
 using content::BrowserThread;
 
@@ -26,26 +20,6 @@
 
 }  // namespace
 
-struct InfoMap::ExtraData {
-  // When the extension was installed.
-  base::Time install_time;
-
-  // True if the user has allowed this extension to run in incognito mode.
-  bool incognito_enabled;
-
-  // True if the user has disabled notifications for this extension manually.
-  bool notifications_disabled;
-
-  ExtraData();
-  ~ExtraData();
-};
-
-InfoMap::ExtraData::ExtraData()
-    : incognito_enabled(false), notifications_disabled(false) {
-}
-
-InfoMap::ExtraData::~ExtraData() {}
-
 InfoMap::InfoMap() {}
 
 const ExtensionSet& InfoMap::extensions() const {
@@ -53,40 +27,23 @@
   return extensions_;
 }
 
-const ExtensionSet& InfoMap::disabled_extensions() const {
-  CheckOnValidThread();
-  return disabled_extensions_;
-}
-
 void InfoMap::AddExtension(const Extension* extension,
                            base::Time install_time,
                            bool incognito_enabled,
                            bool notifications_disabled) {
   CheckOnValidThread();
   extensions_.Insert(extension);
-  disabled_extensions_.Remove(extension->id());
-
-  extra_data_[extension->id()].install_time = install_time;
-  extra_data_[extension->id()].incognito_enabled = incognito_enabled;
-  extra_data_[extension->id()].notifications_disabled = notifications_disabled;
 }
 
 void InfoMap::RemoveExtension(const std::string& extension_id,
                               const UnloadedExtensionReason reason) {
   CheckOnValidThread();
   const Extension* extension = extensions_.GetByID(extension_id);
-  extra_data_.erase(extension_id);  // we don't care about disabled extra data
   bool was_uninstalled = (reason != UnloadedExtensionReason::DISABLE &&
                           reason != UnloadedExtensionReason::TERMINATE);
   if (extension) {
-    if (!was_uninstalled)
-      disabled_extensions_.Insert(extension);
     extensions_.Remove(extension_id);
-  } else if (was_uninstalled) {
-    // If the extension was uninstalled, make sure it's removed from the map of
-    // disabled extensions.
-    disabled_extensions_.Remove(extension_id);
-  } else {
+  } else if (!was_uninstalled) {
     // NOTE: This can currently happen if we receive multiple unload
     // notifications, e.g. setting incognito-enabled state for a
     // disabled extension (e.g., via sync).  See
@@ -95,80 +52,10 @@
   }
 }
 
-base::Time InfoMap::GetInstallTime(const std::string& extension_id) const {
-  auto iter = extra_data_.find(extension_id);
-  if (iter != extra_data_.end())
-    return iter->second.install_time;
-  return base::Time();
-}
-
-bool InfoMap::IsIncognitoEnabled(const std::string& extension_id) const {
-  // Keep in sync with duplicate in extensions/browser/process_manager.cc.
-  auto iter = extra_data_.find(extension_id);
-  if (iter != extra_data_.end())
-    return iter->second.incognito_enabled;
-  return false;
-}
-
-bool InfoMap::CanCrossIncognito(const Extension* extension) const {
-  // This is duplicated from ExtensionService :(.
-  return IsIncognitoEnabled(extension->id()) &&
-         !IncognitoInfo::IsSplitMode(extension);
-}
-
-void InfoMap::RegisterExtensionProcess(const std::string& extension_id,
-                                       int process_id,
-                                       int site_instance_id) {
-  if (!process_map_.Insert(extension_id, process_id, site_instance_id)) {
-    NOTREACHED() << "Duplicate extension process registration for: "
-                 << extension_id << "," << process_id << ".";
-  }
-}
-
-void InfoMap::UnregisterExtensionProcess(const std::string& extension_id,
-                                         int process_id,
-                                         int site_instance_id) {
-  if (!process_map_.Remove(extension_id, process_id, site_instance_id)) {
-    NOTREACHED() << "Unknown extension process registration for: "
-                 << extension_id << "," << process_id << ".";
-  }
-}
-
-void InfoMap::UnregisterAllExtensionsInProcess(int process_id) {
-  process_map_.RemoveAllFromProcess(process_id);
-}
-
-QuotaService* InfoMap::GetQuotaService() {
-  CheckOnValidThread();
-  if (!quota_service_)
-    quota_service_.reset(new QuotaService());
-  return quota_service_.get();
-}
-
-void InfoMap::SetNotificationsDisabled(
-    const std::string& extension_id,
-    bool notifications_disabled) {
-  auto iter = extra_data_.find(extension_id);
-  if (iter != extra_data_.end())
-    iter->second.notifications_disabled = notifications_disabled;
-}
-
-bool InfoMap::AreNotificationsDisabled(
-    const std::string& extension_id) const {
-  auto iter = extra_data_.find(extension_id);
-  if (iter != extra_data_.end())
-    return iter->second.notifications_disabled;
-  return false;
-}
-
 void InfoMap::SetContentVerifier(ContentVerifier* verifier) {
   content_verifier_ = verifier;
 }
 
-void InfoMap::SetIsLockScreenContext(bool is_lock_screen_context) {
-  process_map_.set_is_lock_screen_context(is_lock_screen_context);
-}
-
 InfoMap::~InfoMap() = default;
 
 }  // namespace extensions
diff --git a/extensions/browser/info_map.h b/extensions/browser/info_map.h
index 3c74739..bef7917 100644
--- a/extensions/browser/info_map.h
+++ b/extensions/browser/info_map.h
@@ -11,10 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "content/public/browser/browser_thread.h"
-#include "extensions/browser/process_map.h"
-#include "extensions/browser/quota_service.h"
 #include "extensions/common/extension_set.h"
-#include "extensions/common/permissions/api_permission.h"
 
 namespace extensions {
 class ContentVerifier;
@@ -24,7 +21,6 @@
 // Contains extension data that needs to be accessed on the IO thread. It can
 // be created on any thread, but all other methods and destructor must be called
 // on the IO thread.
-// TODO(http://crbug.com/980774): Audit this to see what is still necessary.
 class InfoMap : public base::RefCountedThreadSafe<
                     InfoMap,
                     content::BrowserThread::DeleteOnIOThread> {
@@ -32,12 +28,9 @@
   InfoMap();
 
   const ExtensionSet& extensions() const;
-  const ExtensionSet& disabled_extensions() const;
-
-  // Information about which extensions are assigned to which render processes.
-  const ProcessMap& process_map() const { return process_map_; }
 
   // Callback for when new extensions are loaded.
+  // TODO(karandeepb): Some of these arguments are unused. Remove them.
   void AddExtension(const Extension* extension,
                     base::Time install_time,
                     bool incognito_enabled,
@@ -47,67 +40,17 @@
   void RemoveExtension(const std::string& extension_id,
                        const UnloadedExtensionReason reason);
 
-  // Returns the time the extension was installed, or base::Time() if not found.
-  base::Time GetInstallTime(const std::string& extension_id) const;
-
-  // Returns true if the user has allowed this extension to run in incognito
-  // mode.
-  bool IsIncognitoEnabled(const std::string& extension_id) const;
-
-  // Returns true if the given extension can see events and data from another
-  // sub-profile (incognito to original profile, or vice versa).
-  bool CanCrossIncognito(const Extension* extension) const;
-
-  // Adds an entry to process_map_.
-  void RegisterExtensionProcess(const std::string& extension_id,
-                                int process_id,
-                                int site_instance_id);
-
-  // Removes an entry from process_map_.
-  void UnregisterExtensionProcess(const std::string& extension_id,
-                                  int process_id,
-                                  int site_instance_id);
-  void UnregisterAllExtensionsInProcess(int process_id);
-
-  // Returns the IO thread QuotaService. Creates the instance on first call.
-  QuotaService* GetQuotaService();
-
-  // Notifications can be enabled/disabled in real time by the user.
-  void SetNotificationsDisabled(const std::string& extension_id,
-                                bool notifications_disabled);
-  bool AreNotificationsDisabled(const std::string& extension_id) const;
-
   void SetContentVerifier(ContentVerifier* verifier);
   ContentVerifier* content_verifier() { return content_verifier_.get(); }
 
-  // Marks the extensions in this info map as running in lock screen context.
-  void SetIsLockScreenContext(bool is_lock_screen_context);
-
  private:
   friend struct content::BrowserThread::DeleteOnThread<
       content::BrowserThread::IO>;
   friend class base::DeleteHelper<InfoMap>;
 
-  // Extra dynamic data related to an extension.
-  struct ExtraData;
-  // Map of extension_id to ExtraData.
-  typedef std::map<std::string, ExtraData> ExtraDataMap;
-
   ~InfoMap();
 
   ExtensionSet extensions_;
-  ExtensionSet disabled_extensions_;
-
-  // Extra data associated with enabled extensions.
-  ExtraDataMap extra_data_;
-
-  // Used by dispatchers to limit API quota for individual extensions.
-  // The QuotaService is not thread safe. We need to create and destroy it on
-  // the IO thread.
-  std::unique_ptr<QuotaService> quota_service_;
-
-  // Assignment of extensions to renderer processes.
-  ProcessMap process_map_;
 
   scoped_refptr<ContentVerifier> content_verifier_;
 };
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index 95c06c0..49f2aa2b 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -173,10 +173,6 @@
     "dependencies": ["permission:dns"],
     "contexts": ["blessed_extension"]
   },
-  "documentScan": {
-    "dependencies": ["permission:documentScan"],
-    "contexts": ["blessed_extension"]
-  },
   // This is not a real API, only here for documentation purposes.
   // See http://crbug.com/275944 for background.
   "extensionTypes": {
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index 265ef14..83e216a2 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -260,11 +260,6 @@
       ]
     }
   ],
-  "documentScan": {
-    "channel": "stable",
-    "extension_types": ["extension", "platform_app"],
-    "platforms": ["chromeos"]
-  },
   "externally_connectable.all_urls": {
     "channel": "stable",
     "extension_types": ["extension"],
diff --git a/extensions/common/api/schema.gni b/extensions/common/api/schema.gni
index 068633b..9dd1b9c 100644
--- a/extensions/common/api/schema.gni
+++ b/extensions/common/api/schema.gni
@@ -64,7 +64,6 @@
   extensions_api_schema_files_ += [
     "crash_report_private.idl",
     "diagnostics.idl",
-    "document_scan.idl",
     "lock_screen_data.idl",
     "media_perception_private.idl",
     "networking_config.idl",
diff --git a/extensions/common/permissions/extensions_api_permissions.cc b/extensions/common/permissions/extensions_api_permissions.cc
index 4d82c54..71c8646 100644
--- a/extensions/common/permissions/extensions_api_permissions.cc
+++ b/extensions/common/permissions/extensions_api_permissions.cc
@@ -57,7 +57,6 @@
      APIPermissionInfo::kFlagCannotBeOptional},
     {APIPermission::kDisplaySource, "displaySource"},
     {APIPermission::kDns, "dns"},
-    {APIPermission::kDocumentScan, "documentScan"},
     {APIPermission::kExternallyConnectableAllUrls,
      "externally_connectable.all_urls",
      APIPermissionInfo::kFlagDoesNotRequireManagedSessionFullLoginWarning},
diff --git a/extensions/shell/browser/shell_content_browser_client.cc b/extensions/shell/browser/shell_content_browser_client.cc
index a19bb64..538d818 100644
--- a/extensions/shell/browser/shell_content_browser_client.cc
+++ b/extensions/shell/browser/shell_content_browser_client.cc
@@ -8,14 +8,11 @@
 
 #include <utility>
 
-#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/stl_util.h"
 #include "components/guest_view/browser/guest_view_message_filter.h"
 #include "components/nacl/common/buildflags.h"
 #include "content/public/browser/browser_main_runner.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/site_instance.h"
@@ -35,7 +32,6 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/guest_view/extensions_guest_view_message_filter.h"
 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
-#include "extensions/browser/info_map.h"
 #include "extensions/browser/process_map.h"
 #include "extensions/browser/url_loader_factory_manager.h"
 #include "extensions/common/constants.h"
@@ -161,13 +157,6 @@
       ->Insert(extension->id(),
                site_instance->GetProcess()->GetID(),
                site_instance->GetId());
-
-  content::GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(&InfoMap::RegisterExtensionProcess,
-                     browser_main_parts_->extension_system()->info_map(),
-                     extension->id(), site_instance->GetProcess()->GetID(),
-                     site_instance->GetId()));
 }
 
 void ShellContentBrowserClient::SiteInstanceDeleting(
@@ -185,13 +174,6 @@
       ->Remove(extension->id(),
                site_instance->GetProcess()->GetID(),
                site_instance->GetId());
-
-  content::GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(&InfoMap::UnregisterExtensionProcess,
-                     browser_main_parts_->extension_system()->info_map(),
-                     extension->id(), site_instance->GetProcess()->GetID(),
-                     site_instance->GetId()));
 }
 
 void ShellContentBrowserClient::AppendExtraCommandLineSwitches(
diff --git a/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc b/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc
index ae2544e..e2e3a66 100644
--- a/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc
+++ b/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc
@@ -406,14 +406,6 @@
   if (!IsApplicable())
     return;
 
-  // TODO(jonahr): Test fails on Linux NVIDIA with ANGLE/passthrough
-  // (crbug.com/1099763)
-  gpu::GPUTestBotConfig bot_config;
-  if (bot_config.LoadCurrentConfig(nullptr) &&
-      (bot_config.Matches("linux nvidia passthrough"))) {
-    return;
-  }
-
   // clang-format off
   static const char* kFragColorShader =
       "#version 300 es\n"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index 83367ad..9a3dd365 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -15308,6 +15308,9 @@
     name: "buildbucket/luci.chromium.try/linux_chromium_ubsan_rel_ng"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-lacros-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux_layout_tests_composite_after_paint"
   }
   builders {
diff --git a/infra/config/generated/luci-notify.cfg b/infra/config/generated/luci-notify.cfg
index 1f0fef8..db90516 100644
--- a/infra/config/generated/luci-notify.cfg
+++ b/infra/config/generated/luci-notify.cfg
@@ -2877,20 +2877,6 @@
 }
 notifiers {
   notifications {
-    on_occurrence: FAILURE
-    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
-    email {
-      rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff"
-    }
-  }
-  notifications {
-    on_occurrence: FAILURE
-    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
-    email {
-      recipients: "thomasanderson@chromium.org"
-    }
-  }
-  notifications {
     on_new_status: FAILURE
     email {
       recipients: "chromium-component-mapping@google.com"
@@ -2900,10 +2886,6 @@
     bucket: "ci"
     name: "linux_chromium_component_updater"
   }
-  tree_closers {
-    tree_status_host: "chromium-status.appspot.com"
-    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
-  }
 }
 notifiers {
   notifications {
@@ -2943,20 +2925,6 @@
 }
 notifiers {
   notifications {
-    on_occurrence: FAILURE
-    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
-    email {
-      rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff"
-    }
-  }
-  notifications {
-    on_occurrence: FAILURE
-    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
-    email {
-      recipients: "thomasanderson@chromium.org"
-    }
-  }
-  notifications {
     on_new_status: FAILURE
     email {
       recipients: "chromium-component-mapping@google.com"
@@ -2965,10 +2933,7 @@
   builders {
     bucket: "ci"
     name: "metadata-exporter"
-  }
-  tree_closers {
-    tree_status_host: "chromium-status.appspot.com"
-    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    repository: "https://chromium.googlesource.com/chromium/src"
   }
 }
 notifiers {
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index e2618fe8..ba595bd 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -10186,6 +10186,15 @@
   }
 }
 job {
+  id: "ci-metadata-exporter"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "metadata-exporter"
+  }
+}
+job {
   id: "ci-win-annotator-rel"
   acl_sets: "ci"
   buildbucket {
@@ -11141,7 +11150,6 @@
 }
 job {
   id: "metadata-exporter"
-  schedule: "0 0,6,12,18 * * *"
   acl_sets: "ci"
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -11865,6 +11873,7 @@
   triggers: "mac-hermetic-upgrade-rel"
   triggers: "mac-mojo-rel"
   triggers: "mac-swangle-chromium-x64"
+  triggers: "metadata-exporter"
   triggers: "win-annotator-rel"
   triggers: "win-archive-dbg"
   triggers: "win-archive-rel"
diff --git a/infra/config/subprojects/chromium/master-only/ci.star b/infra/config/subprojects/chromium/master-only/ci.star
index cca12407..b4ddce4b 100644
--- a/infra/config/subprojects/chromium/master-only/ci.star
+++ b/infra/config/subprojects/chromium/master-only/ci.star
@@ -2924,16 +2924,16 @@
     schedule = '0 0,6,12,18 * * *',
     service_account = 'component-mapping-updater@chops-service-accounts.iam.gserviceaccount.com',
     triggered_by = [],
-    extra_notifies = ['metadata-mapping'],
+    notifies = ['metadata-mapping'],
+    tree_closing = False,
 )
 
 ci.linux_builder(
     name = 'metadata-exporter',
     executable = 'recipe:chromium_export_metadata',
-    schedule = '0 0,6,12,18 * * *',
     service_account = 'component-mapping-updater@chops-service-accounts.iam.gserviceaccount.com',
-    triggered_by = [],
-    extra_notifies = ['metadata-mapping'],
+    notifies = ['metadata-mapping'],
+    tree_closing = False,
 )
 
 ci.mac_ios_builder(
diff --git a/infra/config/subprojects/chromium/master-only/consoles/luci.chromium.try.star b/infra/config/subprojects/chromium/master-only/consoles/luci.chromium.try.star
index 93d7de0..62eb788 100644
--- a/infra/config/subprojects/chromium/master-only/consoles/luci.chromium.try.star
+++ b/infra/config/subprojects/chromium/master-only/consoles/luci.chromium.try.star
@@ -134,6 +134,7 @@
         'try/linux_chromium_msan_rel_ng',
         'try/linux_chromium_tsan_rel_ng',
         'try/linux_chromium_ubsan_rel_ng',
+        'try/linux-lacros-rel',
         'try/linux_layout_tests_composite_after_paint',
         'try/linux-layout-tests-edit-ng',
         'try/linux-layout-tests-fragment-item',
diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm b/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm
index 7d16fbe..dd429d96 100644
--- a/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm
+++ b/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm
@@ -60,6 +60,10 @@
   GURL _safeURL2;
   // Text that is found on the safe page.
   std::string _safeContent2;
+  // The default value for SafeBrowsingEnabled pref.
+  BOOL _safeBrowsingEnabledPrefDefault;
+  // The default value for SafeBrowsingProceedAnywayDisabled pref.
+  BOOL _proceedAnywayDisabledPrefDefault;
 }
 @end
 
@@ -105,8 +109,29 @@
   // GREYAssertTrue cannot be called before [super setUp].
   GREYAssertTrue(started, @"Test server failed to start.");
 
+  // Save the existing value of the pref to set it back in tearDown.
+  _safeBrowsingEnabledPrefDefault =
+      [ChromeEarlGrey userBooleanPref:prefs::kSafeBrowsingEnabled];
   // Ensure that Safe Browsing opt-out starts in its default (opted-in) state.
   [ChromeEarlGrey setBoolValue:YES forUserPref:prefs::kSafeBrowsingEnabled];
+
+  // Save the existing value of the pref to set it back in tearDown.
+  _proceedAnywayDisabledPrefDefault = [ChromeEarlGrey
+      userBooleanPref:prefs::kSafeBrowsingProceedAnywayDisabled];
+  // Ensure that Proceed link is shown by default in the safe browsing warning.
+  [ChromeEarlGrey setBoolValue:NO
+                   forUserPref:prefs::kSafeBrowsingProceedAnywayDisabled];
+}
+
+- (void)tearDown {
+  // Ensure that Safe Browsing is reset to its original value.
+  [ChromeEarlGrey setBoolValue:_safeBrowsingEnabledPrefDefault
+                   forUserPref:prefs::kSafeBrowsingEnabled];
+
+  // Ensure that Proceed link is reset to its original value.
+  [ChromeEarlGrey setBoolValue:_proceedAnywayDisabledPrefDefault
+                   forUserPref:prefs::kSafeBrowsingProceedAnywayDisabled];
+  [super tearDown];
 }
 
 // Tests that safe pages are not blocked.
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_discover_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_discover_item.mm
index ed5feb5..c2523c6 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_discover_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_discover_item.mm
@@ -52,14 +52,6 @@
 
 @implementation ContentSuggestionsDiscoverCell
 
-- (instancetype)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
-  if (self) {
-    self.contentView.backgroundColor = [UIColor redColor];
-  }
-  return self;
-}
-
 - (void)setDiscoverFeedView:(UIViewController*)discoverFeed {
   _discoverFeed = discoverFeed;
   if (discoverFeed) {
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h
index 2a1cb38..06c3fe9 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h
@@ -80,6 +80,9 @@
 // header containing the fake omnibox and the logo.
 - (BOOL)isHeaderSection:(NSInteger)section;
 
+// Returns whether |section| contains the Discover feed.
+- (BOOL)isDiscoverSection:(NSInteger)section;
+
 // Returns whether |section| is one of the section containing ContentSuggestions
 // items.
 - (BOOL)isContentSuggestionsSection:(NSInteger)section;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
index 4834d7c..6653202 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
@@ -569,6 +569,11 @@
              sectionIdentifierForSection:section] == SectionIdentifierLogo;
 }
 
+- (BOOL)isDiscoverSection:(NSInteger)section {
+  return [self.collectionViewController.collectionViewModel
+             sectionIdentifierForSection:section] == SectionIdentifierDiscover;
+}
+
 - (BOOL)isPromoSection:(NSInteger)section {
   return [self.collectionViewController.collectionViewModel
              sectionIdentifierForSection:section] == SectionIdentifierPromo;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index cc33841..140be16 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -436,7 +436,8 @@
   UIEdgeInsets parentInset = [super collectionView:collectionView
                                             layout:collectionViewLayout
                             insetForSectionAtIndex:section];
-  if ([self.collectionUpdater isHeaderSection:section]) {
+  if ([self.collectionUpdater isHeaderSection:section] ||
+      [self.collectionUpdater isDiscoverSection:section]) {
     parentInset.top = 0;
     parentInset.left = 0;
     parentInset.right = 0;
diff --git a/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn b/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn
index c6dc471..f9257b98 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn
+++ b/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn
@@ -38,6 +38,7 @@
     "//ios/chrome/browser/ui/infobars/banners",
     "//ios/chrome/browser/ui/infobars/banners:public",
     "//ios/chrome/browser/ui/infobars/modals",
+    "//ios/chrome/browser/ui/infobars/modals:public",
     "//ios/chrome/browser/ui/infobars/presentation",
     "//ios/chrome/browser/ui/toolbar/public:feature_flags",
     "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
index b2bd241..722d6a94 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
@@ -6,6 +6,8 @@
 #import "ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator+subclassing.h"
 
 #include "base/mac/foundation_util.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
@@ -16,6 +18,7 @@
 #import "ios/chrome/browser/ui/infobars/infobar_badge_ui_delegate.h"
 #import "ios/chrome/browser/ui/infobars/infobar_constants.h"
 #import "ios/chrome/browser/ui/infobars/infobar_container.h"
+#import "ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.h"
 #import "ios/chrome/browser/ui/infobars/presentation/infobar_banner_positioner.h"
 #import "ios/chrome/browser/ui/infobars/presentation/infobar_banner_transition_driver.h"
 #import "ios/chrome/browser/ui/infobars/presentation/infobar_modal_positioner.h"
@@ -313,6 +316,7 @@
 }
 
 - (void)dismissInfobarModal:(id)infobarModal {
+  base::RecordAction(base::UserMetricsAction(kInfobarModalCancelButtonTapped));
   [self dismissInfobarModalAnimated:YES];
 }
 
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.h b/ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.h
index c4d8fcf..414563ed 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.h
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.h
@@ -12,4 +12,7 @@
 // Accessibility identifier of the Modal Cancel Button.
 extern NSString* const kInfobarModalCancelButton;
 
+// UserAction keys.
+extern const char kInfobarModalCancelButtonTapped[];
+
 #endif  // IOS_CHROME_BROWSER_UI_INFOBARS_MODALS_INFOBAR_MODAL_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.mm b/ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.mm
index 5552a28..8454f4a2 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.mm
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.mm
@@ -10,3 +10,5 @@
 
 NSString* const kInfobarModalViewIdentifier = @"kInfobarModalViewIdentifier";
 NSString* const kInfobarModalCancelButton = @"kInfobarModalCancelButton";
+
+const char kInfobarModalCancelButtonTapped[] = "InfobarModalCancelButtonTap";
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_modal/BUILD.gn
index d7feb936..b3e9142 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/BUILD.gn
@@ -49,6 +49,7 @@
     "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/overlays/public/infobar_modal",
     "//ios/chrome/browser/ui/infobars/modals",
+    "//ios/chrome/browser/ui/infobars/modals:public",
     "//ios/chrome/browser/ui/overlays:coordinators",
   ]
 }
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/infobar_modal_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/infobar_modal/infobar_modal_overlay_mediator.mm
index 6afdf7e..5fc8fa0 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/infobar_modal_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/infobar_modal_overlay_mediator.mm
@@ -6,8 +6,11 @@
 
 #import <UIKit/UIKit.h>
 
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "ios/chrome/browser/overlays/public/infobar_modal/infobar_modal_overlay_responses.h"
 #include "ios/chrome/browser/overlays/public/overlay_response.h"
+#import "ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.h"
 #import "ios/chrome/browser/ui/overlays/overlay_request_mediator+subclassing.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -19,6 +22,7 @@
 #pragma mark - InfobarModalDelegate
 
 - (void)dismissInfobarModal:(id)infobarModal {
+  base::RecordAction(base::UserMetricsAction(kInfobarModalCancelButtonTapped));
   [self.delegate stopOverlayForMediator:self];
 }
 
diff --git a/media/fuchsia/audio/BUILD.gn b/media/fuchsia/audio/BUILD.gn
index 23c6d3d..b455c99 100644
--- a/media/fuchsia/audio/BUILD.gn
+++ b/media/fuchsia/audio/BUILD.gn
@@ -8,7 +8,6 @@
   deps = [
     "//base",
     "//media",
-    "//media/fuchsia/mojom",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.media.audio",
     "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
   ]
diff --git a/media/fuchsia/audio/fuchsia_audio_renderer.cc b/media/fuchsia/audio/fuchsia_audio_renderer.cc
index 84da634..bf46fd48 100644
--- a/media/fuchsia/audio/fuchsia_audio_renderer.cc
+++ b/media/fuchsia/audio/fuchsia_audio_renderer.cc
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "base/sequenced_task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/renderer_client.h"
 #include "media/filters/decrypting_demuxer_stream.h"
@@ -78,18 +79,13 @@
 
 FuchsiaAudioRenderer::FuchsiaAudioRenderer(
     MediaLog* media_log,
-    mojo::PendingRemote<media::mojom::FuchsiaMediaResourceProvider>
-        pending_media_resource_provider)
-    : media_log_(media_log) {
+    fidl::InterfaceHandle<fuchsia::media::AudioConsumer> audio_consumer_handle)
+    : media_log_(media_log),
+      audio_consumer_handle_(std::move(audio_consumer_handle)) {
   DETACH_FROM_THREAD(thread_checker_);
-
-  mojo::Remote<media::mojom::FuchsiaMediaResourceProvider>
-      media_resource_provider;
-  media_resource_provider.Bind(std::move(pending_media_resource_provider));
-  media_resource_provider->CreateAudioConsumer(
-      audio_consumer_handle_.NewRequest());
 }
 
+
 FuchsiaAudioRenderer::~FuchsiaAudioRenderer() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
diff --git a/media/fuchsia/audio/fuchsia_audio_renderer.h b/media/fuchsia/audio/fuchsia_audio_renderer.h
index fec88cb1..95ead1af 100644
--- a/media/fuchsia/audio/fuchsia_audio_renderer.h
+++ b/media/fuchsia/audio/fuchsia_audio_renderer.h
@@ -9,13 +9,11 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
+#include "base/timer/timer.h"
 #include "media/base/audio_renderer.h"
 #include "media/base/buffering_state.h"
 #include "media/base/demuxer_stream.h"
 #include "media/base/time_source.h"
-#include "media/fuchsia/mojom/fuchsia_media_resource_provider.mojom.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/remote.h"
 
 namespace media {
 
@@ -27,10 +25,9 @@
 // sends encoded stream directly to AudioConsumer provided by the platform.
 class FuchsiaAudioRenderer : public AudioRenderer, public TimeSource {
  public:
-  FuchsiaAudioRenderer(
-      MediaLog* media_log,
-      mojo::PendingRemote<media::mojom::FuchsiaMediaResourceProvider>
-          media_resource_provider);
+  FuchsiaAudioRenderer(MediaLog* media_log,
+                       fidl::InterfaceHandle<fuchsia::media::AudioConsumer>
+                           audio_consumer_handle);
   ~FuchsiaAudioRenderer() final;
 
   // AudioRenderer implementation.
diff --git a/net/base/net_error_details.h b/net/base/net_error_details.h
index bc0b46e..2f42ed9 100644
--- a/net/base/net_error_details.h
+++ b/net/base/net_error_details.h
@@ -7,7 +7,7 @@
 
 #include "net/base/net_export.h"
 #include "net/http/http_response_info.h"
-#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 
 namespace net {
 
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 49d99801..71cc36f 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -1380,20 +1380,10 @@
 
 bool QuicChromiumClientSession::CanPool(
     const std::string& hostname,
-    PrivacyMode privacy_mode,
-    const SocketTag& socket_tag,
-    const NetworkIsolationKey& network_isolation_key,
-    bool disable_secure_dns) const {
+    const QuicSessionKey& other_session_key) const {
   DCHECK(connection()->connected());
-  if (privacy_mode != session_key_.privacy_mode() ||
-      socket_tag != session_key_.socket_tag() ||
-      (network_isolation_key != session_key_.network_isolation_key() &&
-       base::FeatureList::IsEnabled(
-           features::kPartitionConnectionsByNetworkIsolationKey)) ||
-      disable_secure_dns != session_key_.disable_secure_dns()) {
-    // Privacy mode and socket tag must always match.
+  if (!session_key_.CanUseForAliasing(other_session_key))
     return false;
-  }
   SSLInfo ssl_info;
   if (!GetSSLInfo(&ssl_info) || !ssl_info.cert.get()) {
     NOTREACHED() << "QUIC should always have certificates.";
@@ -1402,7 +1392,7 @@
 
   return SpdySession::CanPool(transport_security_state_, ssl_info,
                               *ssl_config_service_, session_key_.host(),
-                              hostname, network_isolation_key);
+                              hostname, session_key_.network_isolation_key());
 }
 
 bool QuicChromiumClientSession::ShouldCreateIncomingStream(
@@ -3223,9 +3213,7 @@
 }
 
 bool QuicChromiumClientSession::IsAuthorized(const std::string& hostname) {
-  bool result = CanPool(
-      hostname, session_key_.privacy_mode(), session_key_.socket_tag(),
-      session_key_.network_isolation_key(), session_key_.disable_secure_dns());
+  bool result = CanPool(hostname, session_key_);
   if (result)
     streams_pushed_count_++;
   return result;
diff --git a/net/quic/quic_chromium_client_session.h b/net/quic/quic_chromium_client_session.h
index edb004a..a8f14cd 100644
--- a/net/quic/quic_chromium_client_session.h
+++ b/net/quic/quic_chromium_client_session.h
@@ -55,7 +55,6 @@
 class CertVerifyResult;
 class DatagramClientSocket;
 class NetLog;
-class NetworkIsolationKey;
 class QuicCryptoClientStreamFactory;
 class QuicServerInfo;
 class QuicStreamFactory;
@@ -615,14 +614,13 @@
   // than the number of round-trips needed for the handshake.
   int GetNumSentClientHellos() const;
 
-  // Returns true if |hostname| may be pooled onto this session.  If this
-  // is a secure QUIC session, then |hostname| must match the certificate
-  // presented during the handshake.
+  // Returns true if |hostname| may be pooled onto this session.
+  // |other_session_key| specifies the seession key associated with |hostname|
+  // (its own hostname and port fields are ignored). If this is a secure QUIC
+  // session, then |hostname| must match the certificate presented during the
+  // handshake.
   bool CanPool(const std::string& hostname,
-               PrivacyMode privacy_mode,
-               const SocketTag& socket_tag,
-               const NetworkIsolationKey& network_isolation_key,
-               bool disable_secure_dns) const;
+               const QuicSessionKey& other_session_key) const;
 
   const quic::QuicServerId& server_id() const {
     return session_key_.server_id();
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index cbdf851..ced1b46 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -36,6 +36,7 @@
 #include "net/quic/quic_crypto_client_stream_factory.h"
 #include "net/quic/quic_http_utils.h"
 #include "net/quic/quic_server_info.h"
+#include "net/quic/quic_session_key.h"
 #include "net/quic/quic_test_packet_maker.h"
 #include "net/quic/test_quic_crypto_client_config_handle.h"
 #include "net/socket/datagram_client_socket.h"
@@ -1581,34 +1582,42 @@
   CompleteCryptoHandshake();
   session_->OnProofVerifyDetailsAvailable(details);
 
-  EXPECT_TRUE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED,
-                                SocketTag(), NetworkIsolationKey(),
-                                false /* disable_secure_dns */));
-  EXPECT_FALSE(session_->CanPool("www.example.org", PRIVACY_MODE_ENABLED,
-                                 SocketTag(), NetworkIsolationKey(),
-                                 false /* disable_secure_dns */));
-  EXPECT_FALSE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED,
-                                 SocketTag(), NetworkIsolationKey(),
-                                 true /* disable_secure_dns */));
+  EXPECT_TRUE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     NetworkIsolationKey(), false /* disable_secure_dns */)));
+  EXPECT_FALSE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_ENABLED, SocketTag(),
+                     NetworkIsolationKey(), false /* disable_secure_dns */)));
+  EXPECT_FALSE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     NetworkIsolationKey(), true /* disable_secure_dns */)));
 #if defined(OS_ANDROID)
   SocketTag tag1(SocketTag::UNSET_UID, 0x12345678);
   SocketTag tag2(getuid(), 0x87654321);
-  EXPECT_FALSE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED, tag1,
-                                 NetworkIsolationKey(),
-                                 false /* disable_secure_dns */));
-  EXPECT_FALSE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED, tag2,
-                                 NetworkIsolationKey(),
-                                 false /* disable_secure_dns */));
+  EXPECT_FALSE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, tag1,
+                     NetworkIsolationKey(), false /* disable_secure_dns */)));
+  EXPECT_FALSE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, tag2,
+                     NetworkIsolationKey(), false /* disable_secure_dns */)));
 #endif
-  EXPECT_TRUE(session_->CanPool("mail.example.org", PRIVACY_MODE_DISABLED,
-                                SocketTag(), NetworkIsolationKey(),
-                                false /* disable_secure_dns */));
-  EXPECT_TRUE(session_->CanPool("mail.example.com", PRIVACY_MODE_DISABLED,
-                                SocketTag(), NetworkIsolationKey(),
-                                false /* disable_secure_dns */));
-  EXPECT_FALSE(session_->CanPool("mail.google.com", PRIVACY_MODE_DISABLED,
-                                 SocketTag(), NetworkIsolationKey(),
-                                 false /* disable_secure_dns */));
+  EXPECT_TRUE(session_->CanPool(
+      "mail.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     NetworkIsolationKey(), false /* disable_secure_dns */)));
+  EXPECT_TRUE(session_->CanPool(
+      "mail.example.com",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     NetworkIsolationKey(), false /* disable_secure_dns */)));
+  EXPECT_FALSE(session_->CanPool(
+      "mail.google.com",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     NetworkIsolationKey(), false /* disable_secure_dns */)));
 
   const auto kOriginFoo = url::Origin::Create(GURL("http://foo.test/"));
 
@@ -1617,19 +1626,21 @@
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndDisableFeature(
         features::kPartitionConnectionsByNetworkIsolationKey);
-    EXPECT_TRUE(session_->CanPool("mail.example.com", PRIVACY_MODE_DISABLED,
-                                  SocketTag(),
-                                  NetworkIsolationKey(kOriginFoo, kOriginFoo),
-                                  false /* disable_secure_dns */));
+    EXPECT_TRUE(session_->CanPool(
+        "mail.example.com",
+        QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                       NetworkIsolationKey(kOriginFoo, kOriginFoo),
+                       false /* disable_secure_dns */)));
   }
   {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndEnableFeature(
         features::kPartitionConnectionsByNetworkIsolationKey);
-    EXPECT_FALSE(session_->CanPool("mail.example.com", PRIVACY_MODE_DISABLED,
-                                   SocketTag(),
-                                   NetworkIsolationKey(kOriginFoo, kOriginFoo),
-                                   false /* disable_secure_dns */));
+    EXPECT_FALSE(session_->CanPool(
+        "mail.example.com",
+        QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                       NetworkIsolationKey(kOriginFoo, kOriginFoo),
+                       false /* disable_secure_dns */)));
   }
 }
 
@@ -1638,7 +1649,8 @@
   feature_list.InitWithFeatures(
       /* enabled_features */
       {TransportSecurityState::kDynamicExpectCTFeature,
-       features::kPartitionExpectCTStateByNetworkIsolationKey},
+       features::kPartitionExpectCTStateByNetworkIsolationKey,
+       features::kPartitionConnectionsByNetworkIsolationKey},
       /* disabled_features */
       {});
 
@@ -1681,9 +1693,10 @@
   session_->OnProofVerifyDetailsAvailable(details);
 
   // Pooling succeeds if CT isn't required.
-  EXPECT_TRUE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED,
-                                SocketTag(), network_isolation_key,
-                                false /* disable_secure_dns */));
+  EXPECT_TRUE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     network_isolation_key, false /* disable_secure_dns */)));
 
   // Adding Expect-CT data for different NetworkIsolationKeys should have no
   // effect.
@@ -1694,18 +1707,20 @@
   transport_security_state_->AddExpectCT(
       "www.example.org", expiry, true /* enforce */, GURL() /* report_url */,
       NetworkIsolationKey());
-  EXPECT_TRUE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED,
-                                SocketTag(), network_isolation_key,
-                                false /* disable_secure_dns */));
+  EXPECT_TRUE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     network_isolation_key, false /* disable_secure_dns */)));
 
   // Adding Expect-CT data for the same NetworkIsolationKey should prevent
   // pooling.
   transport_security_state_->AddExpectCT(
       "www.example.org", expiry, true /* enforce */, GURL() /* report_url */,
       network_isolation_key);
-  EXPECT_FALSE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED,
-                                 SocketTag(), network_isolation_key,
-                                 false /* disable_secure_dns */));
+  EXPECT_FALSE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     network_isolation_key, false /* disable_secure_dns */)));
 }
 
 // Much as above, but uses a non-empty NetworkIsolationKey.
@@ -1743,38 +1758,47 @@
   CompleteCryptoHandshake();
   session_->OnProofVerifyDetailsAvailable(details);
 
-  EXPECT_TRUE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED,
-                                SocketTag(), kNetworkIsolationKey1,
-                                false /* disable_secure_dns */));
-  EXPECT_FALSE(session_->CanPool("www.example.org", PRIVACY_MODE_ENABLED,
-                                 SocketTag(), kNetworkIsolationKey1,
-                                 false /* disable_secure_dns */));
+  EXPECT_TRUE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     kNetworkIsolationKey1, false /* disable_secure_dns */)));
+  EXPECT_FALSE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_ENABLED, SocketTag(),
+                     kNetworkIsolationKey1, false /* disable_secure_dns */)));
 #if defined(OS_ANDROID)
   SocketTag tag1(SocketTag::UNSET_UID, 0x12345678);
   SocketTag tag2(getuid(), 0x87654321);
-  EXPECT_FALSE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED, tag1,
-                                 kNetworkIsolationKey1,
-                                 false /* disable_secure_dns */));
-  EXPECT_FALSE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED, tag2,
-                                 kNetworkIsolationKey1,
-                                 false /* disable_secure_dns */));
+  EXPECT_FALSE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, tag1,
+                     kNetworkIsolationKey1, false /* disable_secure_dns */)));
+  EXPECT_FALSE(session_->CanPool(
+      "www.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, tag2,
+                     kNetworkIsolationKey1, false /* disable_secure_dns */)));
 #endif
-  EXPECT_TRUE(session_->CanPool("mail.example.org", PRIVACY_MODE_DISABLED,
-                                SocketTag(), kNetworkIsolationKey1,
-                                false /* disable_secure_dns */));
-  EXPECT_TRUE(session_->CanPool("mail.example.com", PRIVACY_MODE_DISABLED,
-                                SocketTag(), kNetworkIsolationKey1,
-                                false /* disable_secure_dns */));
-  EXPECT_FALSE(session_->CanPool("mail.google.com", PRIVACY_MODE_DISABLED,
-                                 SocketTag(), kNetworkIsolationKey1,
-                                 false /* disable_secure_dns */));
+  EXPECT_TRUE(session_->CanPool(
+      "mail.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     kNetworkIsolationKey1, false /* disable_secure_dns */)));
+  EXPECT_TRUE(session_->CanPool(
+      "mail.example.com",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     kNetworkIsolationKey1, false /* disable_secure_dns */)));
+  EXPECT_FALSE(session_->CanPool(
+      "mail.google.com",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     kNetworkIsolationKey1, false /* disable_secure_dns */)));
 
-  EXPECT_FALSE(session_->CanPool("mail.example.com", PRIVACY_MODE_DISABLED,
-                                 SocketTag(), kNetworkIsolationKey2,
-                                 false /* disable_secure_dns */));
-  EXPECT_FALSE(session_->CanPool("mail.example.com", PRIVACY_MODE_DISABLED,
-                                 SocketTag(), NetworkIsolationKey(),
-                                 false /* disable_secure_dns */));
+  EXPECT_FALSE(session_->CanPool(
+      "mail.example.com",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     kNetworkIsolationKey2, false /* disable_secure_dns */)));
+  EXPECT_FALSE(session_->CanPool(
+      "mail.example.com",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     NetworkIsolationKey(), false /* disable_secure_dns */)));
 }
 
 TEST_P(QuicChromiumClientSessionTest, ConnectionNotPooledWithDifferentPin) {
@@ -1814,9 +1838,10 @@
   session_->OnProofVerifyDetailsAvailable(details);
   QuicChromiumClientSessionPeer::SetHostname(session_.get(), kNoPinsHost);
 
-  EXPECT_FALSE(session_->CanPool(kPreloadedPKPHost, PRIVACY_MODE_DISABLED,
-                                 SocketTag(), NetworkIsolationKey(),
-                                 false /* disable_secure_dns */));
+  EXPECT_FALSE(session_->CanPool(
+      kPreloadedPKPHost,
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     NetworkIsolationKey(), false /* disable_secure_dns */)));
 }
 
 TEST_P(QuicChromiumClientSessionTest, ConnectionPooledWithMatchingPin) {
@@ -1847,9 +1872,10 @@
   session_->OnProofVerifyDetailsAvailable(details);
   QuicChromiumClientSessionPeer::SetHostname(session_.get(), "www.example.org");
 
-  EXPECT_TRUE(session_->CanPool("mail.example.org", PRIVACY_MODE_DISABLED,
-                                SocketTag(), NetworkIsolationKey(),
-                                false /* disable_secure_dns */));
+  EXPECT_TRUE(session_->CanPool(
+      "mail.example.org",
+      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
+                     NetworkIsolationKey(), false /* disable_secure_dns */)));
 }
 
 TEST_P(QuicChromiumClientSessionTest, MigrateToSocket) {
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index fb8a297..741bf3f 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -819,7 +819,6 @@
     hanging_data->set_connect_data(hanging_connect);
     hanging_data_.push_back(std::move(hanging_data));
     socket_factory_.AddSocketDataProvider(hanging_data_.back().get());
-    socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
   }
 
   void SetUpTestForRetryConnectionOnAlternateNetwork() {
diff --git a/net/quic/quic_session_key.cc b/net/quic/quic_session_key.cc
index ff1a8f52..69706c6b 100644
--- a/net/quic/quic_session_key.cc
+++ b/net/quic/quic_session_key.cc
@@ -67,6 +67,14 @@
          disable_secure_dns_ == other.disable_secure_dns_;
 }
 
+bool QuicSessionKey::CanUseForAliasing(const QuicSessionKey& other) const {
+  return server_id_.privacy_mode_enabled() ==
+             other.server_id_.privacy_mode_enabled() &&
+         socket_tag_ == other.socket_tag_ &&
+         network_isolation_key_ == other.network_isolation_key_ &&
+         disable_secure_dns_ == other.disable_secure_dns_;
+}
+
 size_t QuicSessionKey::EstimateMemoryUsage() const {
   return server_id_.EstimateMemoryUsage();
 }
diff --git a/net/quic/quic_session_key.h b/net/quic/quic_session_key.h
index a561aab..c91ebd5 100644
--- a/net/quic/quic_session_key.h
+++ b/net/quic/quic_session_key.h
@@ -40,6 +40,16 @@
   bool operator<(const QuicSessionKey& other) const;
   bool operator==(const QuicSessionKey& other) const;
 
+  // Checks if requests using QuicSessionKey can potentially be used to service
+  // requests using another.  Returns true if all fields except QuicServerId's
+  // host and port match. The caller *MUST* also make sure that the session
+  // associated with one key has been verified for use with the host/port of the
+  // other.
+  //
+  // Note that this method is symmetric, so it doesn't matter which key's method
+  // is called on the other.
+  bool CanUseForAliasing(const QuicSessionKey& other) const;
+
   const std::string& host() const { return server_id_.host(); }
 
   PrivacyMode privacy_mode() const {
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 99cd7353..6a7fd51 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -1153,10 +1153,7 @@
   for (const auto& key_value : active_sessions_) {
     QuicChromiumClientSession* session = key_value.second;
     if (destination.Equals(all_sessions_[session].destination()) &&
-        session->CanPool(session_key.host(), session_key.privacy_mode(),
-                         session_key.socket_tag(),
-                         session_key.network_isolation_key(),
-                         session_key.disable_secure_dns())) {
+        session->CanPool(session_key.host(), session_key)) {
       return true;
     }
   }
@@ -1181,16 +1178,16 @@
              .Equals(HostPortPair::FromURL(url)));
   // Enforce session affinity for promised streams.
   //
-  // TODO(https://crbug.com/1105544): This logic should also handle
-  // NetworkIsolationKey.
+  // TODO(https://crbug.com/1105544): Both having one promise per URL globally
+  // and deleting them on mismatch leaks data between NetworkIsolationKeys. Fix
+  // that.
   quic::QuicClientPromisedInfo* promised =
       push_promise_index_.GetPromised(url.spec());
   if (promised) {
     QuicChromiumClientSession* session =
         static_cast<QuicChromiumClientSession*>(promised->session());
     DCHECK(session);
-    if (session->quic_session_key().privacy_mode() ==
-        session_key.privacy_mode()) {
+    if (session->quic_session_key().CanUseForAliasing(session_key)) {
       request->SetSession(session->CreateHandle(destination));
       ++num_push_streams_created_;
       return OK;
@@ -1231,10 +1228,7 @@
     for (const auto& key_value : active_sessions_) {
       QuicChromiumClientSession* session = key_value.second;
       if (destination.Equals(all_sessions_[session].destination()) &&
-          session->CanPool(session_key.server_id().host(),
-                           session_key.privacy_mode(), session_key.socket_tag(),
-                           session_key.network_isolation_key(),
-                           session_key.disable_secure_dns())) {
+          session->CanPool(session_key.server_id().host(), session_key)) {
         request->SetSession(session->CreateHandle(destination));
         return OK;
       }
@@ -1673,12 +1667,8 @@
 
     const SessionSet& sessions = ip_aliases_[address];
     for (QuicChromiumClientSession* session : sessions) {
-      if (!session->CanPool(server_id.host(), key.session_key().privacy_mode(),
-                            key.session_key().socket_tag(),
-                            key.session_key().network_isolation_key(),
-                            key.session_key().disable_secure_dns())) {
+      if (!session->CanPool(server_id.host(), key.session_key()))
         continue;
-      }
       active_sessions_[key.session_key()] = session;
       session_aliases_[session].insert(key);
       return true;
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 9cb945e..edf246d9 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -11384,6 +11384,95 @@
   EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
 }
 
+TEST_P(QuicStreamFactoryTest, ServerPushNetworkIsolationKeyMismatch) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      // enabled_features
+      {features::kPartitionHttpServerPropertiesByNetworkIsolationKey,
+       // Need to partition connections by NetworkIsolationKey for
+       // QuicSessionAliasKey to include NetworkIsolationKeys.
+       features::kPartitionConnectionsByNetworkIsolationKey},
+      // disabled_features
+      {});
+
+  Initialize();
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  MockQuicData socket_data1(version_);
+  socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  int packet_num = 1;
+  if (VersionUsesHttp3(version_.transport_version)) {
+    socket_data1.AddWrite(SYNCHRONOUS,
+                          ConstructInitialSettingsPacket(packet_num++));
+  }
+  socket_data1.AddWrite(
+      SYNCHRONOUS,
+      client_maker_.MakeRstPacket(
+          packet_num++, true, GetNthServerInitiatedUnidirectionalStreamId(0),
+          quic::QUIC_STREAM_CANCELLED));
+  socket_data1.AddSocketDataToFactory(socket_factory_.get());
+
+  client_maker_.Reset();
+  MockQuicData socket_data2(version_);
+  socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  if (VersionUsesHttp3(version_.transport_version))
+    socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+  socket_data2.AddSocketDataToFactory(socket_factory_.get());
+
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(
+      ERR_IO_PENDING,
+      request.Request(
+          host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+          SocketTag(), NetworkIsolationKey(), false /* disable_secure_dns */,
+          /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+          failed_on_default_network_callback_, callback_.callback()));
+
+  EXPECT_THAT(callback_.WaitForResult(), IsOk());
+  std::unique_ptr<HttpStream> stream = CreateStream(&request);
+  EXPECT_TRUE(stream.get());
+
+  EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumPushStreamsCreated(factory_.get()));
+
+  string url = "https://www.example.org/";
+  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+
+  quic::QuicClientPromisedInfo promised(
+      session, GetNthServerInitiatedUnidirectionalStreamId(0), kDefaultUrl);
+
+  quic::QuicClientPushPromiseIndex* index =
+      QuicStreamFactoryPeer::GetPushPromiseIndex(factory_.get());
+
+  (*index->promised_by_url())[kDefaultUrl] = &promised;
+  EXPECT_EQ(index->GetPromised(kDefaultUrl), &promised);
+
+  // Doing the request should not use the push stream, but rather
+  // cancel it because the NetworkIsolationKeys do not match.
+  QuicStreamRequest request2(factory_.get());
+  EXPECT_EQ(
+      ERR_IO_PENDING,
+      request2.Request(
+          host_port_pair_, version_, PRIVACY_MODE_DISABLED, DEFAULT_PRIORITY,
+          SocketTag(), NetworkIsolationKey::CreateTransient(),
+          false /* disable_secure_dns */,
+          /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+          failed_on_default_network_callback_, callback_.callback()));
+
+  EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumPushStreamsCreated(factory_.get()));
+  EXPECT_EQ(index->GetPromised(kDefaultUrl), nullptr);
+
+  EXPECT_THAT(callback_.WaitForResult(), IsOk());
+  std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
+  EXPECT_TRUE(stream2.get());
+
+  EXPECT_TRUE(socket_data1.AllReadDataConsumed());
+  EXPECT_TRUE(socket_data1.AllWriteDataConsumed());
+  EXPECT_TRUE(socket_data2.AllReadDataConsumed());
+  EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
+}
+
 // Pool to existing session with matching quic::QuicServerId
 // even if destination is different.
 TEST_P(QuicStreamFactoryTest, PoolByOrigin) {
diff --git a/remoting/android/BUILD.gn b/remoting/android/BUILD.gn
index 9ecf7ee3..00c0eba 100644
--- a/remoting/android/BUILD.gn
+++ b/remoting/android/BUILD.gn
@@ -181,6 +181,7 @@
     ":remoting_android_client_java",
     "//base:base_java",
     "//base:base_java_test_support",
+    "//third_party/android_deps:androidx_test_runner_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/junit:junit",
   ]
diff --git a/testing/buildbot/README.md b/testing/buildbot/README.md
index e2fad5b..7062ae54 100644
--- a/testing/buildbot/README.md
+++ b/testing/buildbot/README.md
@@ -163,8 +163,8 @@
   name.
 
 There are other arguments specific to other test types (script tests, JUnit
-tests, instrumentation tests, CTS tests); consult the generator script and
-test_suites.pyl for more details and examples.
+tests); consult the generator script and test_suites.pyl for more details and
+examples.
 
 ### Compound test suites
 #### Composition test suites
@@ -338,16 +338,20 @@
   tests. The value is a string referring either to a basic or composition test
   suite from [test_suites.pyl](./test_suites.pyl).
 
-    * `cts_tests`: (Android-specific) conformance test suites.
-    * `gtest_tests`: GTest-based tests.
-    * `instrumentation_tests`: (Android-specific) instrumentation tests.
+    * `gtest_tests`: GTest-based tests (or other kinds of tests that
+       emulate the GTest-based API), which can be run either locally or
+       under Swarming.
     * `isolated_scripts`: Isolated script tests. These are bundled into an
        isolate, invoke a wrapper script from src/testing/scripts as their
        top-level entry point, and are used to adapt to multiple kinds of test
-       harnesses.
-    * `junit_tests`: (Android-specific) JUnit tests.
-    * `scripts`: Legacy script tests living in src/testing/scripts. These can
-       not be Swarmed, and further use is discouraged.
+       harnesses. These must implement the
+       [Test Executable API](//docs/testing/test_executable_api.md) and
+       can also be run either locally or under Swarming.
+    * `junit_tests`: (Android-specific) JUnit tests. These are not run
+       under Swarming.
+    * `scripts`: Legacy script tests living in src/testing/scripts. These
+       also are not (and usually can not) be run under Swarming. These
+       types of tests are strongly discouraged.
 
 * `swarming`: a dictionary specifying Swarming parameters to be applied to all
   tests that run on the bot.
diff --git a/testing/buildbot/chromium.ci.json b/testing/buildbot/chromium.ci.json
index b057d95..9413e23 100644
--- a/testing/buildbot/chromium.ci.json
+++ b/testing/buildbot/chromium.ci.json
@@ -1009,7 +1009,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -1034,7 +1034,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1059,7 +1059,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1081,7 +1081,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1103,7 +1103,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1122,7 +1122,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1158,7 +1158,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1184,7 +1184,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1215,7 +1215,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1246,7 +1246,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1278,7 +1278,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1309,7 +1309,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -22590,7 +22590,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -22616,7 +22616,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -22641,7 +22641,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -22667,7 +22667,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -22693,7 +22693,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -22718,7 +22718,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -22743,7 +22743,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -22766,7 +22766,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -22787,7 +22787,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -22816,7 +22816,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -22842,7 +22842,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -22869,7 +22869,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23224,7 +23224,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23250,7 +23250,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23275,7 +23275,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23301,7 +23301,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23327,7 +23327,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23352,7 +23352,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23377,7 +23377,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23400,7 +23400,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23421,7 +23421,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23450,7 +23450,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23476,7 +23476,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -23503,7 +23503,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -58983,7 +58983,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59008,7 +59008,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59032,7 +59032,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59054,7 +59054,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59086,7 +59086,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59116,7 +59116,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59146,7 +59146,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59176,7 +59176,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59210,7 +59210,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59249,7 +59249,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59288,7 +59288,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59319,7 +59319,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59349,7 +59349,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59380,7 +59380,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59411,7 +59411,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -59436,7 +59436,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59461,7 +59461,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59485,7 +59485,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59507,7 +59507,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59529,7 +59529,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59548,7 +59548,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59571,7 +59571,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59590,7 +59590,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -59622,7 +59622,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59652,7 +59652,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59682,7 +59682,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59712,7 +59712,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59746,7 +59746,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59785,7 +59785,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59824,7 +59824,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59855,7 +59855,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59885,7 +59885,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -59916,7 +59916,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -61844,7 +61844,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -61869,7 +61869,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -61895,7 +61895,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -61920,7 +61920,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -61944,7 +61944,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -61966,7 +61966,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -61988,7 +61988,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -62007,7 +62007,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -62030,7 +62030,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -62049,7 +62049,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -62081,7 +62081,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62111,7 +62111,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62141,7 +62141,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62171,7 +62171,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62201,7 +62201,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62235,7 +62235,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62274,7 +62274,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62313,7 +62313,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62352,7 +62352,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62391,7 +62391,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62416,7 +62416,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -62446,7 +62446,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62477,7 +62477,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62507,7 +62507,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62539,7 +62539,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62572,7 +62572,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62603,7 +62603,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62635,7 +62635,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -62666,7 +62666,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -65713,7 +65713,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -65739,7 +65739,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -65764,7 +65764,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -65790,7 +65790,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -65815,7 +65815,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -66648,7 +66648,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -66674,7 +66674,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -66699,7 +66699,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -66723,7 +66723,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -66745,7 +66745,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -66777,7 +66777,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -66807,7 +66807,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -66837,7 +66837,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -66867,7 +66867,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -66901,7 +66901,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -66940,7 +66940,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -66979,7 +66979,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67004,7 +67004,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -67034,7 +67034,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67064,7 +67064,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67095,7 +67095,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67125,7 +67125,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -67152,7 +67152,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -67178,7 +67178,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -67203,7 +67203,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -67226,7 +67226,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -67259,7 +67259,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67290,7 +67290,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67321,7 +67321,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67352,7 +67352,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67387,7 +67387,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67427,7 +67427,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67467,7 +67467,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67493,7 +67493,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -67524,7 +67524,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67555,7 +67555,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -67587,7 +67587,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -90753,7 +90753,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -90776,7 +90776,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -90795,7 +90795,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -90818,7 +90818,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -90837,7 +90837,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -90869,7 +90869,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -90899,7 +90899,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -90929,7 +90929,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -90959,7 +90959,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -90993,7 +90993,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -91032,7 +91032,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -91071,7 +91071,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -91102,7 +91102,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -91132,7 +91132,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -91164,7 +91164,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -91195,7 +91195,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -91227,7 +91227,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -228091,7 +228091,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228132,7 +228133,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228173,7 +228175,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228214,7 +228217,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228255,7 +228259,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228296,7 +228301,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228337,7 +228343,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228378,7 +228385,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228419,7 +228427,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228460,7 +228469,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228501,7 +228511,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228542,7 +228553,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228583,7 +228595,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228624,7 +228637,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228665,7 +228679,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228706,7 +228721,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228747,7 +228763,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228788,7 +228805,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228829,7 +228847,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228871,7 +228890,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228913,7 +228933,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228955,7 +228976,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -228997,7 +229019,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229039,7 +229062,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229081,7 +229105,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229123,7 +229148,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229165,7 +229191,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229206,7 +229233,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229247,7 +229275,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229288,7 +229317,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229329,7 +229359,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229370,7 +229401,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229411,7 +229443,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229452,7 +229485,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229493,7 +229527,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229535,7 +229570,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229577,7 +229613,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229619,7 +229656,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229661,7 +229699,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229702,7 +229741,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229743,7 +229783,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229784,7 +229825,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229825,7 +229867,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229866,7 +229909,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229907,7 +229951,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229948,7 +229993,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -229989,7 +230035,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230030,7 +230077,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230071,7 +230119,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230112,7 +230161,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230153,7 +230203,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230194,7 +230245,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230235,7 +230287,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230276,7 +230329,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230317,7 +230371,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230358,7 +230413,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230399,7 +230455,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230440,7 +230497,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230481,7 +230539,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230522,7 +230581,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230563,7 +230623,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230604,7 +230665,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230645,7 +230707,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230686,7 +230749,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230727,7 +230791,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230768,7 +230833,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230809,7 +230875,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230850,7 +230917,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230891,7 +230959,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230932,7 +231001,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -230973,7 +231043,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231014,7 +231085,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231055,7 +231127,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231096,7 +231169,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231137,7 +231211,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231178,7 +231253,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231219,7 +231295,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231260,7 +231337,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231301,7 +231379,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231349,7 +231428,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231390,7 +231470,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231431,7 +231512,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231472,7 +231554,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231513,7 +231596,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231554,7 +231638,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231595,7 +231680,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231636,7 +231722,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231677,7 +231764,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231718,7 +231806,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231759,7 +231848,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231800,7 +231890,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231841,7 +231932,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231882,7 +231974,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231923,7 +232016,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -231964,7 +232058,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232005,7 +232100,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232046,7 +232142,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232087,7 +232184,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232128,7 +232226,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232169,7 +232268,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232210,7 +232310,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232251,7 +232352,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232292,7 +232394,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232333,7 +232436,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232374,7 +232478,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232415,7 +232520,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232456,7 +232562,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232497,7 +232604,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232538,7 +232646,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232579,7 +232688,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232620,7 +232730,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232661,7 +232772,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232702,7 +232814,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232743,7 +232856,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232784,7 +232898,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232825,7 +232940,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232866,7 +232982,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232907,7 +233024,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232948,7 +233066,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -232989,7 +233108,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233030,7 +233150,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233071,7 +233192,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233112,7 +233234,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233153,7 +233276,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233194,7 +233318,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233236,7 +233361,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233278,7 +233404,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233320,7 +233447,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233362,7 +233490,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233404,7 +233533,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233446,7 +233576,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233488,7 +233619,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233530,7 +233662,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233572,7 +233705,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233614,7 +233748,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233656,7 +233791,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233698,7 +233834,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233740,7 +233877,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233782,7 +233920,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233824,7 +233963,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233866,7 +234006,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233907,7 +234048,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233948,7 +234090,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -233989,7 +234132,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234030,7 +234174,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234071,7 +234216,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234112,7 +234258,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234153,7 +234300,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234194,7 +234342,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234235,7 +234384,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234276,7 +234426,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234317,7 +234468,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234358,7 +234510,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234399,7 +234552,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234440,7 +234594,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234481,7 +234636,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234522,7 +234678,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234564,7 +234721,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234606,7 +234764,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234648,7 +234807,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234690,7 +234850,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234732,7 +234893,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234774,7 +234936,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234816,7 +234979,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234857,7 +235021,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234898,7 +235063,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234939,7 +235105,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -234980,7 +235147,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235021,7 +235189,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235062,7 +235231,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235103,7 +235273,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235144,7 +235315,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235185,7 +235357,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235226,7 +235399,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235267,7 +235441,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235308,7 +235483,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235349,7 +235525,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235390,7 +235567,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235431,7 +235609,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235472,7 +235651,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235513,7 +235693,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235554,7 +235735,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235595,7 +235777,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235636,7 +235819,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235677,7 +235861,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235718,7 +235903,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235759,7 +235945,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235800,7 +235987,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235841,7 +236029,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235882,7 +236071,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235923,7 +236113,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -235964,7 +236155,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236005,7 +236197,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236046,7 +236239,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236087,7 +236281,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236128,7 +236323,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236169,7 +236365,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236210,7 +236407,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236251,7 +236449,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236292,7 +236491,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236333,7 +236533,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236374,7 +236575,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236415,7 +236617,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236456,7 +236659,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236497,7 +236701,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236538,7 +236743,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236579,7 +236785,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236620,7 +236827,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236661,7 +236869,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236702,7 +236911,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236743,7 +236953,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236784,7 +236995,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236825,7 +237037,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236866,7 +237079,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236907,7 +237121,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236948,7 +237163,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -236989,7 +237205,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237030,7 +237247,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237071,7 +237289,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237112,7 +237331,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237153,7 +237373,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237194,7 +237415,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237235,7 +237457,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237276,7 +237499,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237317,7 +237541,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237358,7 +237583,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237399,7 +237625,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237440,7 +237667,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237481,7 +237709,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237522,7 +237751,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237563,7 +237793,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237604,7 +237835,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237645,7 +237877,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237686,7 +237919,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237727,7 +237961,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237768,7 +238003,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237809,7 +238045,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237850,7 +238087,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237891,7 +238129,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237932,7 +238171,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -237973,7 +238213,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238014,7 +238255,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238055,7 +238297,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238096,7 +238339,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238137,7 +238381,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238178,7 +238423,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238219,7 +238465,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238260,7 +238507,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238301,7 +238549,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238342,7 +238591,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238383,7 +238633,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238424,7 +238675,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238465,7 +238717,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238506,7 +238759,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238547,7 +238801,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238588,7 +238843,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238629,7 +238885,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238670,7 +238927,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238711,7 +238969,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238752,7 +239011,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -238793,7 +239053,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
diff --git a/testing/buildbot/chromium.dawn.json b/testing/buildbot/chromium.dawn.json
index fb4121e..bf83af5f 100644
--- a/testing/buildbot/chromium.dawn.json
+++ b/testing/buildbot/chromium.dawn.json
@@ -1639,7 +1639,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1665,7 +1665,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1690,7 +1690,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1716,7 +1716,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1742,7 +1742,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1767,7 +1767,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1792,7 +1792,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1815,7 +1815,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1836,7 +1836,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1865,7 +1865,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1891,7 +1891,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1918,7 +1918,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2273,7 +2273,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2299,7 +2299,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2324,7 +2324,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2350,7 +2350,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2376,7 +2376,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2401,7 +2401,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2426,7 +2426,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2449,7 +2449,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2470,7 +2470,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2499,7 +2499,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2525,7 +2525,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2552,7 +2552,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 955047c..39ebabd 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -37677,7 +37677,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -37718,7 +37719,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -37759,7 +37761,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -37800,7 +37803,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -37841,7 +37845,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -37882,7 +37887,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -37923,7 +37929,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -37964,7 +37971,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38005,7 +38013,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38046,7 +38055,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38087,7 +38097,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38128,7 +38139,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38169,7 +38181,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38210,7 +38223,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38251,7 +38265,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38292,7 +38307,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38333,7 +38349,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38374,7 +38391,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38415,7 +38433,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38457,7 +38476,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38499,7 +38519,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38541,7 +38562,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38583,7 +38605,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38625,7 +38648,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38667,7 +38691,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38709,7 +38734,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38751,7 +38777,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38792,7 +38819,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38833,7 +38861,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38874,7 +38903,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38915,7 +38945,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38956,7 +38987,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -38997,7 +39029,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39038,7 +39071,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39079,7 +39113,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39121,7 +39156,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39163,7 +39199,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39205,7 +39242,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39247,7 +39285,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39288,7 +39327,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39329,7 +39369,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39370,7 +39411,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39411,7 +39453,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39452,7 +39495,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39493,7 +39537,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39534,7 +39579,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39575,7 +39621,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39616,7 +39663,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39657,7 +39705,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39698,7 +39747,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39739,7 +39789,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39780,7 +39831,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39821,7 +39873,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39862,7 +39915,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39903,7 +39957,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39944,7 +39999,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -39985,7 +40041,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40026,7 +40083,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40067,7 +40125,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40108,7 +40167,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40149,7 +40209,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40190,7 +40251,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40231,7 +40293,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40272,7 +40335,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40313,7 +40377,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40354,7 +40419,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40395,7 +40461,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40436,7 +40503,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40477,7 +40545,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40518,7 +40587,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40559,7 +40629,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40600,7 +40671,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40641,7 +40713,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40682,7 +40755,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40723,7 +40797,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40764,7 +40839,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40805,7 +40881,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40846,7 +40923,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40887,7 +40965,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40935,7 +41014,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -40976,7 +41056,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41017,7 +41098,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41058,7 +41140,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41099,7 +41182,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41140,7 +41224,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41181,7 +41266,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41222,7 +41308,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41263,7 +41350,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41304,7 +41392,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41345,7 +41434,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41386,7 +41476,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41427,7 +41518,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41468,7 +41560,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41509,7 +41602,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41550,7 +41644,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41591,7 +41686,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41632,7 +41728,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41673,7 +41770,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41714,7 +41812,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41755,7 +41854,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41796,7 +41896,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41837,7 +41938,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41878,7 +41980,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41919,7 +42022,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -41960,7 +42064,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42001,7 +42106,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42042,7 +42148,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42083,7 +42190,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42124,7 +42232,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42165,7 +42274,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42206,7 +42316,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42247,7 +42358,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42288,7 +42400,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42329,7 +42442,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42370,7 +42484,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42411,7 +42526,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42452,7 +42568,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42493,7 +42610,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42534,7 +42652,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42575,7 +42694,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42616,7 +42736,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42657,7 +42778,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42698,7 +42820,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42739,7 +42862,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42780,7 +42904,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42822,7 +42947,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42864,7 +42990,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42906,7 +43033,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42948,7 +43076,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -42990,7 +43119,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43032,7 +43162,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43074,7 +43205,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43116,7 +43248,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43158,7 +43291,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43200,7 +43334,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43242,7 +43377,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43284,7 +43420,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43326,7 +43463,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43368,7 +43506,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43410,7 +43549,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43452,7 +43592,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43493,7 +43634,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43534,7 +43676,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43575,7 +43718,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43616,7 +43760,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43657,7 +43802,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43698,7 +43844,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43739,7 +43886,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43780,7 +43928,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43821,7 +43970,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43862,7 +44012,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43903,7 +44054,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43944,7 +44096,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -43985,7 +44138,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44026,7 +44180,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44067,7 +44222,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44108,7 +44264,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44150,7 +44307,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44192,7 +44350,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44234,7 +44393,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44276,7 +44436,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44318,7 +44479,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44360,7 +44522,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44402,7 +44565,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44443,7 +44607,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44484,7 +44649,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44525,7 +44691,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44566,7 +44733,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44607,7 +44775,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44648,7 +44817,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44689,7 +44859,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44730,7 +44901,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44771,7 +44943,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44812,7 +44985,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44853,7 +45027,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44894,7 +45069,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44935,7 +45111,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -44976,7 +45153,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45017,7 +45195,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45058,7 +45237,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45099,7 +45279,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45140,7 +45321,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45181,7 +45363,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45222,7 +45405,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45263,7 +45447,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45304,7 +45489,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45345,7 +45531,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45386,7 +45573,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45427,7 +45615,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45468,7 +45657,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45509,7 +45699,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45550,7 +45741,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45591,7 +45783,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45632,7 +45825,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45673,7 +45867,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45714,7 +45909,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45755,7 +45951,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45796,7 +45993,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45837,7 +46035,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45878,7 +46077,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45919,7 +46119,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -45960,7 +46161,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46001,7 +46203,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46042,7 +46245,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46083,7 +46287,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46124,7 +46329,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46165,7 +46371,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46206,7 +46413,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46247,7 +46455,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46288,7 +46497,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46329,7 +46539,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46370,7 +46581,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46411,7 +46623,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46452,7 +46665,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46493,7 +46707,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46534,7 +46749,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46575,7 +46791,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46616,7 +46833,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46657,7 +46875,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46698,7 +46917,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46739,7 +46959,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46780,7 +47001,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46821,7 +47043,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46862,7 +47085,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46903,7 +47127,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46944,7 +47169,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -46985,7 +47211,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47026,7 +47253,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47067,7 +47295,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47108,7 +47337,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47149,7 +47379,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47190,7 +47421,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47231,7 +47463,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47272,7 +47505,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47313,7 +47547,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47354,7 +47589,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47395,7 +47631,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47436,7 +47673,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47477,7 +47715,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47518,7 +47757,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47559,7 +47799,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47600,7 +47841,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47641,7 +47883,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47682,7 +47925,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47723,7 +47967,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47764,7 +48009,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47805,7 +48051,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47846,7 +48093,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47887,7 +48135,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47928,7 +48177,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -47969,7 +48219,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -48010,7 +48261,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -48051,7 +48303,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -48092,7 +48345,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -48133,7 +48387,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -48174,7 +48429,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -48215,7 +48471,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -48256,7 +48513,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -48297,7 +48555,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -48338,7 +48597,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
@@ -48379,7 +48639,8 @@
           ],
           "dimension_sets": [
             {
-              "os": "Mac-10.15"
+              "os": "Mac-10.15",
+              "pool": "chromium.tests.ios14"
             }
           ],
           "named_caches": [
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index f51a7e7..ce85e3ba 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -1009,7 +1009,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -1034,7 +1034,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1059,7 +1059,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1081,7 +1081,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1103,7 +1103,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1122,7 +1122,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1158,7 +1158,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1184,7 +1184,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1215,7 +1215,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1246,7 +1246,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1278,7 +1278,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1309,7 +1309,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -13818,7 +13818,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -13843,7 +13843,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13868,7 +13868,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13892,7 +13892,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13914,7 +13914,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13936,7 +13936,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13955,7 +13955,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13978,7 +13978,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -13997,7 +13997,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -14029,7 +14029,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -14059,7 +14059,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -14089,7 +14089,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -14119,7 +14119,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -14153,7 +14153,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -14192,7 +14192,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -14231,7 +14231,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -14262,7 +14262,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -14292,7 +14292,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -14323,7 +14323,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16251,7 +16251,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -16276,7 +16276,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -16302,7 +16302,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -16327,7 +16327,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -16351,7 +16351,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -16373,7 +16373,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -16395,7 +16395,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -16414,7 +16414,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -16437,7 +16437,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -16456,7 +16456,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -16488,7 +16488,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16518,7 +16518,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16548,7 +16548,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16578,7 +16578,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16608,7 +16608,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16642,7 +16642,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16681,7 +16681,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16720,7 +16720,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16759,7 +16759,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16798,7 +16798,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16823,7 +16823,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -16853,7 +16853,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16884,7 +16884,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16914,7 +16914,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16946,7 +16946,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -16979,7 +16979,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -17010,7 +17010,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -17042,7 +17042,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -17073,7 +17073,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -20120,7 +20120,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -20146,7 +20146,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -20171,7 +20171,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -20197,7 +20197,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -20222,7 +20222,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -21556,7 +21556,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -21579,7 +21579,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -21598,7 +21598,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -21621,7 +21621,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -21640,7 +21640,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -21672,7 +21672,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -21702,7 +21702,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -21732,7 +21732,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -21762,7 +21762,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -21796,7 +21796,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -21835,7 +21835,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -21874,7 +21874,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -21905,7 +21905,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -21935,7 +21935,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -21967,7 +21967,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -21998,7 +21998,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -22030,7 +22030,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
diff --git a/testing/buildbot/chromium.gpu.json b/testing/buildbot/chromium.gpu.json
index 4ea9917..3a5e165a 100644
--- a/testing/buildbot/chromium.gpu.json
+++ b/testing/buildbot/chromium.gpu.json
@@ -1639,7 +1639,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1664,7 +1664,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1688,7 +1688,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1710,7 +1710,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1742,7 +1742,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1772,7 +1772,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1802,7 +1802,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1832,7 +1832,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1866,7 +1866,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1905,7 +1905,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1944,7 +1944,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1975,7 +1975,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2005,7 +2005,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2036,7 +2036,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2065,7 +2065,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2091,7 +2091,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2116,7 +2116,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2140,7 +2140,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2162,7 +2162,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2194,7 +2194,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2224,7 +2224,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2254,7 +2254,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2284,7 +2284,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2318,7 +2318,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2357,7 +2357,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2396,7 +2396,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2421,7 +2421,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2451,7 +2451,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2481,7 +2481,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2512,7 +2512,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2542,7 +2542,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2569,7 +2569,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2595,7 +2595,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2620,7 +2620,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2643,7 +2643,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2676,7 +2676,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2707,7 +2707,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2738,7 +2738,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2769,7 +2769,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2804,7 +2804,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2844,7 +2844,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2884,7 +2884,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2910,7 +2910,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -2941,7 +2941,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2972,7 +2972,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -3004,7 +3004,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 8882b988..b8d8592a 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -193,7 +193,8 @@
     "additional_compile_targets": [
       "microdump_stackwalk",
       "angle_perftests",
-      "chrome_apk"
+      "chrome_apk",
+      "system_webview_google_apk"
     ],
     "isolated_scripts": [
       {
@@ -645,7 +646,8 @@
     "additional_compile_targets": [
       "microdump_stackwalk",
       "angle_perftests",
-      "chrome_apk"
+      "chrome_apk",
+      "system_webview_google_apk"
     ],
     "isolated_scripts": [
       {
diff --git a/testing/buildbot/client.v8.fyi.json b/testing/buildbot/client.v8.fyi.json
index e9bff85..e6a54f0 100644
--- a/testing/buildbot/client.v8.fyi.json
+++ b/testing/buildbot/client.v8.fyi.json
@@ -1017,7 +1017,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1046,7 +1046,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1075,7 +1075,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1104,7 +1104,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1142,7 +1142,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1180,7 +1180,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1210,7 +1210,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1239,7 +1239,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1270,7 +1270,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -1301,7 +1301,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
diff --git a/testing/buildbot/filters/fuchsia.components_unittests.filter b/testing/buildbot/filters/fuchsia.components_unittests.filter
index db823ee..b33d3c7 100644
--- a/testing/buildbot/filters/fuchsia.components_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.components_unittests.filter
@@ -32,6 +32,7 @@
 -DownloadFile/DownloadFileTestWithRename.*
 -DownloadPathReservationTrackerTest.UnwriteableDirectory
 -FaviconCacheTest.*
+-FaviconDatabaseTest.*
 -FileManagerTest.*
 -GoogleNewLogoApiTest.ParsesCapturedApiResult
 -HistoryBackendDBTest.*
@@ -75,7 +76,6 @@
 -SuggestionMatchingTest.*
 -SyncBookmarkDataTypeControllerTest.*
 -SyncedBookmarkTrackerTest.*
--ThumbnailDatabaseTest.*
 -TopSitesDatabaseTest.*
 -TranslateManagerTest.*
 -TranslatePrefsTest.*
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index e0842935..996078d 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -172,38 +172,6 @@
     return sorted(tests, key=lambda x: x['test'])
 
 
-class CTSGenerator(BaseGenerator):
-  def __init__(self, bb_gen):
-    super(CTSGenerator, self).__init__(bb_gen)
-
-  def generate(self, waterfall, tester_name, tester_config, input_tests):
-    # These only contain one entry and it's the contents of the input tests'
-    # dictionary, verbatim.
-    cts_tests = []
-    cts_tests.append(input_tests)
-    return cts_tests
-
-  def sort(self, tests):
-    return tests
-
-
-class InstrumentationTestGenerator(BaseGenerator):
-  def __init__(self, bb_gen):
-    super(InstrumentationTestGenerator, self).__init__(bb_gen)
-
-  def generate(self, waterfall, tester_name, tester_config, input_tests):
-    scripts = []
-    for test_name, test_config in sorted(input_tests.iteritems()):
-      test = self.bb_gen.generate_instrumentation_test(
-        waterfall, tester_name, tester_config, test_name, test_config)
-      if test:
-        scripts.append(test)
-    return scripts
-
-  def sort(self, tests):
-    return sorted(tests, cmp=cmp_tests)
-
-
 def check_compound_references(other_test_suites=None,
                               sub_suite=None,
                               suite=None,
@@ -839,21 +807,6 @@
     self.substitute_magic_args(result)
     return result
 
-  def generate_instrumentation_test(self, waterfall, tester_name, tester_config,
-                                    test_name, test_config):
-    if not self.should_run_on_tester(waterfall, tester_name, test_name,
-                                     test_config):
-      return None
-    result = copy.deepcopy(test_config)
-    if 'test' in result and result['test'] != test_name:
-      result['name'] = test_name
-    else:
-      result['test'] = test_name
-    result = self.update_and_cleanup_test(
-        result, test_name, tester_name, tester_config, waterfall)
-    self.substitute_magic_args(result)
-    return result
-
   def substitute_gpu_args(self, tester_config, swarming_config, args):
     substitutions = {
       # Any machine in waterfalls.pyl which desires to run GPU tests
@@ -931,14 +884,10 @@
     return {
         'android_webview_gpu_telemetry_tests':
             GPUTelemetryTestGenerator(self, is_android_webview=True),
-        'cts_tests':
-            CTSGenerator(self),
         'gpu_telemetry_tests':
             GPUTelemetryTestGenerator(self),
         'gtest_tests':
             GTestGenerator(self),
-        'instrumentation_tests':
-            InstrumentationTestGenerator(self),
         'isolated_scripts':
             IsolatedScriptTestGenerator(self),
         'junit_tests':
@@ -1003,10 +952,7 @@
   def resolve_test_id_prefixes(self):
     for suite in self.test_suites['basic_suites'].itervalues():
       for key, test in suite.iteritems():
-        if not isinstance(test, dict):
-          # Some test definitions are just strings, such as CTS.
-          # Skip them.
-          continue
+        assert isinstance(test, dict)
 
         # This assumes the recipe logic which prefers 'test' to 'isolate_name'
         # https://source.chromium.org/chromium/chromium/tools/build/+/master:scripts/slave/recipe_modules/chromium_tests/generators.py;l=89;drc=14c062ba0eb418d3c4623dde41a753241b9df06b
@@ -1586,11 +1532,7 @@
         continue
 
       for test in suite.values():
-        if not isinstance(test, dict):
-          # Some test suites have top level keys, which currently can't be
-          # swarming mixin entries. Ignore them
-          continue
-
+        assert isinstance(test, dict)
         seen_mixins = seen_mixins.union(test.get('mixins', set()))
 
     missing_mixins = set(self.mixins.keys()) - seen_mixins
diff --git a/testing/buildbot/generate_buildbot_json_unittest.py b/testing/buildbot/generate_buildbot_json_unittest.py
index 28e603019..be15727 100755
--- a/testing/buildbot/generate_buildbot_json_unittest.py
+++ b/testing/buildbot/generate_buildbot_json_unittest.py
@@ -239,6 +239,24 @@
 ]
 """
 
+FOO_ISOLATED_SCRIPTS_WATERFALL_ANDROID = """\
+[
+  {
+    'project': 'chromium',
+    'bucket': 'ci',
+    'name': 'chromium.test',
+    'machines': {
+      'Fake Tester': {
+        'test_suites': {
+          'isolated_scripts': 'composition_tests',
+        },
+        'use_android_presentation': True,
+      },
+    },
+  },
+]
+"""
+
 FOO_SCRIPT_WATERFALL = """\
 [
   {
@@ -309,58 +327,6 @@
 ]
 """
 
-FOO_CTS_WATERFALL = """\
-[
-  {
-    'project': 'chromium',
-    'bucket': 'ci',
-    'name': 'chromium.test',
-    'machines': {
-      'Fake Tester': {
-        'test_suites': {
-          'cts_tests': 'foo_cts_tests',
-        },
-      },
-    },
-  },
-]
-"""
-
-FOO_ISOLATED_CTS_WATERFALL = """\
-[
-  {
-    'project': 'chromium',
-    'bucket': 'ci',
-    'name': 'chromium.test',
-    'machines': {
-      'Fake Tester': {
-        'test_suites': {
-          'isolated_scripts': 'isolated_foo_cts_tests',
-        },
-        'use_android_presentation': True,
-      },
-    },
-  },
-]
-"""
-
-FOO_INSTRUMENTATION_TEST_WATERFALL = """\
-[
-  {
-    'project': 'chromium',
-    'bucket': 'ci',
-    'name': 'chromium.test',
-    'machines': {
-      'Fake Tester': {
-        'test_suites': {
-          'instrumentation_tests': 'composition_tests',
-        },
-      },
-    },
-  },
-]
-"""
-
 FOO_GPU_TELEMETRY_TEST_WATERFALL = """\
 [
   {
@@ -761,27 +727,6 @@
 }
 """
 
-FOO_CTS_SUITE = """\
-{
-  'basic_suites': {
-    'foo_cts_tests': {
-      'arch': 'arm64',
-      'platform': 'L',
-    },
-  },
-}
-"""
-
-FOO_ISOLATED_CTS_SUITE = """\
-{
-  'basic_suites': {
-    'isolated_foo_cts_tests': {
-      'foo_cts_tests': {},
-    },
-  },
-}
-"""
-
 GOOD_COMPOSITION_TEST_SUITES = """\
 {
   'basic_suites': {
@@ -865,21 +810,6 @@
 }
 """
 
-INSTRUMENTATION_TESTS_WITH_DIFFERENT_NAMES = """\
-{
-  'basic_suites': {
-    'composition_tests': {
-      'foo_tests': {
-        'test': 'foo_test',
-      },
-      'bar_tests': {
-        'test': 'foo_test',
-      },
-    },
-  },
-}
-"""
-
 SCRIPT_SUITE = """\
 {
   'basic_suites': {
@@ -1427,6 +1357,53 @@
 }
 """
 
+ISOLATED_SCRIPT_OUTPUT_ANDROID = """\
+{
+  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
+  "AAAAA2 See generate_buildbot_json.py to make changes": {},
+  "Fake Tester": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details"
+        ],
+        "isolate_name": "foo_test",
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "foo_test"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "foo_test",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        }
+      }
+    ]
+  }
+}
+"""
+
 SCRIPT_OUTPUT = """\
 {
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
@@ -1475,105 +1452,6 @@
 }
 """
 
-CTS_OUTPUT = """\
-{
-  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
-  "AAAAA2 See generate_buildbot_json.py to make changes": {},
-  "Fake Tester": {
-    "cts_tests": [
-      {
-        "arch": "arm64",
-        "platform": "L"
-      }
-    ]
-  }
-}
-"""
-
-CTS_ISOLATED_OUTPUT = """\
-{
-  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
-  "AAAAA2 See generate_buildbot_json.py to make changes": {},
-  "Fake Tester": {
-    "isolated_scripts": [
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details"
-        ],
-        "isolate_name": "foo_cts_tests",
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "foo_cts_tests"
-          ],
-          "script": \
-"//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "foo_cts_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": \
-"git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        }
-      }
-    ]
-  }
-}
-"""
-
-INSTRUMENTATION_TEST_OUTPUT = """\
-{
-  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
-  "AAAAA2 See generate_buildbot_json.py to make changes": {},
-  "Fake Tester": {
-    "instrumentation_tests": [
-      {
-        "test": "foo_test"
-      }
-    ]
-  }
-}
-"""
-
-INSTRUMENTATION_TEST_DIFFERENT_NAMES_OUTPUT = """\
-{
-  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
-  "AAAAA2 See generate_buildbot_json.py to make changes": {},
-  "Fake Tester": {
-    "instrumentation_tests": [
-      {
-        "name": "bar_tests",
-        "test": "foo_test",
-        "test_id_prefix": "ninja://chrome/test:foo_test/"
-      },
-      {
-        "name": "foo_tests",
-        "test": "foo_test",
-        "test_id_prefix": "ninja://chrome/test:foo_test/"
-      }
-    ]
-  }
-}
-"""
-
 GPU_TELEMETRY_TEST_OUTPUT = """\
 {
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
@@ -1947,7 +1825,7 @@
     'machines': {
       'Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
@@ -1965,13 +1843,13 @@
     'machines': {
       'Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
       'Really Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
@@ -1984,13 +1862,13 @@
     'machines': {
       'Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
       'Really Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
@@ -2008,13 +1886,13 @@
     'machines': {
       'Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
       'Really Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
@@ -2027,13 +1905,13 @@
     'machines': {
       'Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
       'Really Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
@@ -2051,13 +1929,13 @@
     'machines': {
       'Really Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
       'Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
@@ -2070,13 +1948,13 @@
     'machines': {
       'Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
       'Really Fake Tester': {
         'test_suites': {
-          'instrumentation_tests': 'suite_a',
+          'gtest_tests': 'suite_a',
           'scripts': 'suite_b',
         },
       },
@@ -2088,8 +1966,8 @@
 # Note that the suites in basic_suites would be sorted after the suites in
 # compound_suites. This is valid though, because each set of suites is sorted
 # separately.
-# suite_c is an 'instrumentation_tests' test
-# suite_d is an 'scripts' test
+# suite_c is a 'gtest_tests' test
+# suite_d is a 'scripts' test
 TEST_SUITE_SORTED = """\
 {
   'basic_suites': {
@@ -2650,6 +2528,19 @@
     fbb.check_output_file_consistency(verbose=True)
     self.assertFalse(fbb.printed_lines)
 
+  def test_isolated_script_tests(self):
+    fbb = FakeBBGen(self.args,
+                    FOO_ISOLATED_SCRIPTS_WATERFALL_ANDROID,
+                    GOOD_COMPOSITION_TEST_SUITES,
+                    LUCI_MILO_CFG,
+                    exceptions=NO_BAR_TEST_EXCEPTIONS)
+    self.create_testing_buildbot_json_file('chromium.test.json',
+                                           ISOLATED_SCRIPT_OUTPUT_ANDROID)
+    self.create_testing_buildbot_json_file('chromium.ci.json',
+                                           ISOLATED_SCRIPT_OUTPUT_ANDROID)
+    fbb.check_output_file_consistency(verbose=True)
+    self.assertFalse(fbb.printed_lines)
+
   def test_script_with_args(self):
     fbb = FakeBBGen(self.args,
                     FOO_SCRIPT_WATERFALL,
@@ -2705,36 +2596,6 @@
     fbb.check_output_file_consistency(verbose=True)
     self.assertFalse(fbb.printed_lines)
 
-  def test_cts_tests(self):
-    fbb = FakeBBGen(self.args, FOO_CTS_WATERFALL, FOO_CTS_SUITE, LUCI_MILO_CFG)
-    self.create_testing_buildbot_json_file('chromium.test.json', CTS_OUTPUT)
-    self.create_testing_buildbot_json_file('chromium.ci.json', CTS_OUTPUT)
-    fbb.check_output_file_consistency(verbose=True)
-    self.assertFalse(fbb.printed_lines)
-
-  def test_isolated_cts_tests(self):
-    fbb = FakeBBGen(self.args, FOO_ISOLATED_CTS_WATERFALL,
-                    FOO_ISOLATED_CTS_SUITE, LUCI_MILO_CFG)
-    self.create_testing_buildbot_json_file('chromium.test.json',
-                                           CTS_ISOLATED_OUTPUT)
-    self.create_testing_buildbot_json_file('chromium.ci.json',
-                                           CTS_ISOLATED_OUTPUT)
-    fbb.check_output_file_consistency(verbose=True)
-    self.assertFalse(fbb.printed_lines)
-
-  def test_instrumentation_tests(self):
-    fbb = FakeBBGen(self.args,
-                    FOO_INSTRUMENTATION_TEST_WATERFALL,
-                    GOOD_COMPOSITION_TEST_SUITES,
-                    LUCI_MILO_CFG,
-                    exceptions=NO_BAR_TEST_EXCEPTIONS)
-    self.create_testing_buildbot_json_file('chromium.test.json',
-                                           INSTRUMENTATION_TEST_OUTPUT)
-    self.create_testing_buildbot_json_file('chromium.ci.json',
-                                           INSTRUMENTATION_TEST_OUTPUT)
-    fbb.check_output_file_consistency(verbose=True)
-    self.assertFalse(fbb.printed_lines)
-
   def test_gpu_telemetry_tests(self):
     fbb = FakeBBGen(self.args,
                     FOO_GPU_TELEMETRY_TEST_WATERFALL,
@@ -2791,19 +2652,6 @@
     fbb.check_output_file_consistency(verbose=True)
     self.assertFalse(fbb.printed_lines)
 
-  def test_instrumentation_tests_with_different_names(self):
-    fbb = FakeBBGen(self.args,
-                    FOO_INSTRUMENTATION_TEST_WATERFALL,
-                    INSTRUMENTATION_TESTS_WITH_DIFFERENT_NAMES,
-                    LUCI_MILO_CFG,
-                    gn_isolate_map=GN_ISOLATE_MAP)
-    self.create_testing_buildbot_json_file(
-        'chromium.test.json', INSTRUMENTATION_TEST_DIFFERENT_NAMES_OUTPUT)
-    self.create_testing_buildbot_json_file(
-        'chromium.ci.json', INSTRUMENTATION_TEST_DIFFERENT_NAMES_OUTPUT)
-    fbb.check_output_file_consistency(verbose=True)
-    self.assertFalse(fbb.printed_lines)
-
   def test_ungenerated_output_files_are_caught(self):
     fbb = FakeBBGen(self.args,
                     COMPOSITION_GTEST_SUITE_WATERFALL,
@@ -3506,24 +3354,6 @@
 }
 """
 
-FOO_CTS_WATERFALL_MIXINS = """\
-[
-  {
-    'project': 'chromium',
-    'bucket': 'ci',
-    'name': 'chromium.test',
-    'machines': {
-      'Fake Tester': {
-        'mixins': ['test_mixin'],
-        'test_suites': {
-          'cts_tests': 'foo_cts_tests',
-        },
-      },
-    },
-  },
-]
-"""
-
 WATERFALL_DIMENSION_SETS_WATERFALL_OUTPUT = """\
 {
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
@@ -3908,15 +3738,6 @@
       fbb.check_input_file_consistency(verbose=True)
     self.assertFalse(fbb.printed_lines)
 
-  def test_cts(self):
-    fbb = FakeBBGen(self.args, FOO_CTS_WATERFALL_MIXINS, FOO_CTS_SUITE,
-                    LUCI_MILO_CFG)
-    self.create_testing_buildbot_json_file('chromium.test.json', CTS_OUTPUT)
-    self.create_testing_buildbot_json_file('chromium.ci.json', CTS_OUTPUT)
-    fbb.check_input_file_consistency(verbose=True)
-    fbb.check_output_file_consistency(verbose=True)
-    self.assertFalse(fbb.printed_lines)
-
   def test_unused(self):
     fbb = FakeBBGen(self.args,
                     FOO_GTESTS_INVALID_NOTFOUND_MIXIN_WATERFALL,
diff --git a/testing/buildbot/manage.py b/testing/buildbot/manage.py
index fd2a5c9..ba48567 100755
--- a/testing/buildbot/manage.py
+++ b/testing/buildbot/manage.py
@@ -65,133 +65,136 @@
 
 
 SKIP_GN_ISOLATE_MAP_TARGETS = {
-  # This target is magic and not present in gn_isolate_map.pyl.
-  'all',
-  'remoting/client:client',
-  'remoting/host:host',
+    # This target is magic and not present in gn_isolate_map.pyl.
+    'all',
+    'remoting/client:client',
+    'remoting/host:host',
 
-  # These targets are listed only in build-side recipes.
-  'All_syzygy',
-  'blink_tests',
-  'captured_sites_interactive_tests',
-  'cast_shell',
-  'cast_shell_apk',
-  'chrome_official_builder',
-  'chrome_official_builder_no_unittests',
-  'chrome_sandbox',
-  'chromium_builder_asan',
-  'chromium_builder_perf',
-  'chromiumos_preflight',
-  'linux_symbols',
-  'mini_installer',
-  'previous_version_mini_installer',
-  'symupload',
+    # These targets are listed only in build-side recipes.
+    'All_syzygy',
+    'blink_tests',
+    'captured_sites_interactive_tests',
+    'cast_shell',
+    'cast_shell_apk',
+    'chrome_official_builder',
+    'chrome_official_builder_no_unittests',
+    'chrome_sandbox',
+    'chromium_builder_asan',
+    'chromium_builder_perf',
+    'chromiumos_preflight',
+    'linux_symbols',
+    'mini_installer',
+    'previous_version_mini_installer',
+    'symupload',
 
-  # iOS tests are listed in //ios/build/bots.
-  'cronet_test',
-  'cronet_unittests_ios',
-  'ios_chrome_bookmarks_eg2tests_module',
-  'ios_chrome_bookmarks_egtests',
-  'ios_chrome_integration_eg2tests_module',
-  'ios_chrome_integration_egtests',
-  'ios_chrome_reading_list_egtests',
-  'ios_chrome_settings_eg2tests_module',
-  'ios_chrome_settings_egtests',
-  'ios_chrome_signin_eg2tests_module',
-  'ios_chrome_signin_egtests',
-  'ios_chrome_smoke_eg2tests_module',
-  'ios_chrome_smoke_egtests',
-  'ios_chrome_translate_egtests',
-  'ios_chrome_ui_eg2tests_module',
-  'ios_chrome_ui_egtests',
-  'ios_chrome_unittests',
-  'ios_chrome_web_eg2tests_module',
-  'ios_chrome_web_egtests',
-  'ios_components_unittests',
-  'ios_crash_xcuitests_module',
-  'ios_net_unittests',
-  'ios_remoting_unittests',
-  'ios_showcase_eg2tests_module',
-  'ios_showcase_egtests',
-  'ios_testing_unittests',
-  'ios_web_inttests',
-  'ios_web_shell_eg2tests_module',
-  'ios_web_shell_egtests',
-  'ios_web_unittests',
-  'ios_web_view_inttests',
-  'ios_web_view_unittests',
+    # iOS tests are listed in //ios/build/bots.
+    'cronet_test',
+    'cronet_unittests_ios',
+    'ios_chrome_bookmarks_eg2tests_module',
+    'ios_chrome_bookmarks_egtests',
+    'ios_chrome_integration_eg2tests_module',
+    'ios_chrome_integration_egtests',
+    'ios_chrome_reading_list_egtests',
+    'ios_chrome_settings_eg2tests_module',
+    'ios_chrome_settings_egtests',
+    'ios_chrome_signin_eg2tests_module',
+    'ios_chrome_signin_egtests',
+    'ios_chrome_smoke_eg2tests_module',
+    'ios_chrome_smoke_egtests',
+    'ios_chrome_translate_egtests',
+    'ios_chrome_ui_eg2tests_module',
+    'ios_chrome_ui_egtests',
+    'ios_chrome_unittests',
+    'ios_chrome_web_eg2tests_module',
+    'ios_chrome_web_egtests',
+    'ios_components_unittests',
+    'ios_crash_xcuitests_module',
+    'ios_net_unittests',
+    'ios_remoting_unittests',
+    'ios_showcase_eg2tests_module',
+    'ios_showcase_egtests',
+    'ios_testing_unittests',
+    'ios_web_inttests',
+    'ios_web_shell_eg2tests_module',
+    'ios_web_shell_egtests',
+    'ios_web_unittests',
+    'ios_web_view_inttests',
+    'ios_web_view_unittests',
 
-  # These are listed in Builders that are skipped for other reasons.
-  'chrome_junit_tests',
-  'components_background_task_scheduler_junit_tests',
-  'components_embedder_support_junit_tests',
-  'components_gcm_driver_junit_tests',
-  'components_permissions_junit_tests',
-  'components_policy_junit_tests',
-  'components_variations_junit_tests',
-  'content_junit_tests',
-  'content_junit_tests',
-  'device_junit_tests',
-  'junit_unit_tests',
-  'keyboard_accessory_junit_tests',
-  'media_router_e2e_tests',
-  'media_router_junit_tests',
-  'media_router_perf_tests',
-  'net_junit_tests',
-  'net_junit_tests',
-  'password_check_junit_tests',
-  'service_junit_tests',
-  'shipped_binaries',
-  'system_webview_apk',
-  'touch_to_fill_junit_tests',
-  'ui_junit_tests',
-  'vr_common_perftests',
-  'vr_perf_tests',
-  'vrcore_fps_test',
-  'webapk_client_junit_tests',
-  'webapk_shell_apk_h2o_junit_tests',
-  'webapk_shell_apk_junit_tests',
+    # These are listed in Builders that are skipped for other reasons.
+    'chrome_junit_tests',
+    'components_background_task_scheduler_junit_tests',
+    'components_embedder_support_junit_tests',
+    'components_gcm_driver_junit_tests',
+    'components_permissions_junit_tests',
+    'components_policy_junit_tests',
+    'components_variations_junit_tests',
+    'content_junit_tests',
+    'content_junit_tests',
+    'device_junit_tests',
+    'junit_unit_tests',
+    'keyboard_accessory_junit_tests',
+    'media_router_e2e_tests',
+    'media_router_junit_tests',
+    'media_router_perf_tests',
+    'net_junit_tests',
+    'net_junit_tests',
+    'password_check_junit_tests',
+    'service_junit_tests',
+    'shipped_binaries',
+    'system_webview_apk',
+    'touch_to_fill_junit_tests',
+    'ui_junit_tests',
+    'vr_common_perftests',
+    'vr_perf_tests',
+    'vrcore_fps_test',
+    'webapk_client_junit_tests',
+    'webapk_shell_apk_h2o_junit_tests',
+    'webapk_shell_apk_junit_tests',
 
-  # These tests are only run on WebRTC CI.
-  'AppRTCMobileTest',
-  'android_examples_junit_tests',
-  'android_sdk_junit_tests',
-  'audio_decoder_unittests',
-  'common_audio_unittests',
-  'common_video_unittests',
-  'libjingle_peerconnection_android_unittest',
-  'modules_tests',
-  'modules_unittests',
-  'peerconnection_unittests',
-  'rtc_media_unittests',
-  'rtc_pc_unittests',
-  'rtc_stats_unittests',
-  'rtc_unittests',
-  'system_wrappers_unittests',
-  'test_support_unittests',
-  'tools_unittests',
-  'video_engine_tests',
-  'voice_engine_unittests',
-  'voip_unittests',
-  'webrtc_nonparallel_tests',
-  'xmllite_xmpp_unittests',
+    # These tests are only run on WebRTC CI.
+    'AppRTCMobileTest',
+    'android_examples_junit_tests',
+    'android_sdk_junit_tests',
+    'audio_decoder_unittests',
+    'common_audio_unittests',
+    'common_video_unittests',
+    'libjingle_peerconnection_android_unittest',
+    'modules_tests',
+    'modules_unittests',
+    'peerconnection_unittests',
+    'rtc_media_unittests',
+    'rtc_pc_unittests',
+    'rtc_stats_unittests',
+    'rtc_unittests',
+    'system_wrappers_unittests',
+    'test_support_unittests',
+    'tools_unittests',
+    'video_engine_tests',
+    'voice_engine_unittests',
+    'voip_unittests',
+    'webrtc_nonparallel_tests',
+    'xmllite_xmpp_unittests',
 
-  # These are only run on V8 CI.
-  'pdfium_test',
-  'postmortem-metadata',
+    # These are only run on V8 CI.
+    'pdfium_test',
+    'postmortem-metadata',
 
-  # These are only for developer convenience and not on any bots.
-  'telemetry_gpu_integration_test_scripts_only',
+    # These are only for developer convenience and not on any bots.
+    'telemetry_gpu_integration_test_scripts_only',
 
-  # These are defined by an android internal gn_isolate_map.pyl file.
-  'chrome_apk',
-  'resource_sizes_chrome_modern_minimal_apks',
-  'resource_sizes_monochrome_minimal_apks',
-  'resource_sizes_trichrome_google',
-  'resource_sizes_system_webview_google_bundle',
+    # These are defined by an android internal gn_isolate_map.pyl file.
+    'resource_sizes_chrome_modern_minimal_apks',
+    'resource_sizes_monochrome_minimal_apks',
+    'resource_sizes_trichrome_google',
+    'resource_sizes_system_webview_google_bundle',
 
-  # These are used by https://www.chromium.org/developers/cluster-telemetry.
-  'ct_telemetry_perf_tests_without_chrome',
+    # These are only used by perf bots.
+    'chrome_apk',
+    'system_webview_google_apk',
+
+    # These are used by https://www.chromium.org/developers/cluster-telemetry.
+    'ct_telemetry_perf_tests_without_chrome',
 }
 
 
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 0ab5219b..2a13c11 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -207,6 +207,13 @@
       'service_account': 'chromium-tester@chops-service-accounts.iam.gserviceaccount.com',
     },
   },
+  'chromium-tests-ios14': {
+    'swarming': {
+      'dimensions': {
+        'pool': 'chromium.tests.ios14',
+      },
+    },
+  },
   # TODO(crbug.com/1057152): Add this mixin unconditionally for all CrOS device
   # tests.
   'cros_device_flash': {
@@ -496,7 +503,7 @@
     'swarming': {
       'dimensions': {
         'gpu': '8086:0a2e',
-        'os': 'Mac-10.14.6',
+        'os': 'Mac-10.14.6|Mac-10.15.5',
       },
     },
   },
diff --git a/testing/buildbot/v8.ci.json b/testing/buildbot/v8.ci.json
index 583caf7..6657f4b 100644
--- a/testing/buildbot/v8.ci.json
+++ b/testing/buildbot/v8.ci.json
@@ -2164,7 +2164,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2193,7 +2193,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2222,7 +2222,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2251,7 +2251,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2289,7 +2289,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2327,7 +2327,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2357,7 +2357,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2386,7 +2386,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2417,7 +2417,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
@@ -2448,7 +2448,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:0a2e",
-              "os": "Mac-10.14.6"
+              "os": "Mac-10.14.6|Mac-10.15.5"
             }
           ],
           "idempotent": false,
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index b8d676c..cea80595 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2104,6 +2104,7 @@
           'all',
         ],
         'mixins': [
+          'chromium-tests-ios14',
           'mac_10.15',
           'mac_toolchain',
           'out_dir_arg',
@@ -2120,6 +2121,7 @@
           'all',
         ],
         'mixins': [
+          'chromium-tests-ios14',
           'mac_10.15',
           'mac_toolchain',
           'out_dir_arg',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index c8ded4b..cafa13d 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2956,6 +2956,21 @@
             ]
         }
     ],
+    "FullscreenBrowserScope": [
+        {
+            "platforms": [
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "FullscreenControllerBrowserScoped"
+                    ]
+                }
+            ]
+        }
+    ],
     "FuzzyAppSearch": [
         {
             "platforms": [
@@ -4754,7 +4769,7 @@
             ]
         }
     ],
-    "OmniboxSearchEngineLogo": [
+    "OmniboxSearchEngineLogoAndroid": [
         {
             "platforms": [
                 "android"
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index 88f08202..60f9238 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -467,6 +467,20 @@
 }
 
 # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
+android_aar_prebuilt("androidx_media_media_java") {
+  aar_path = "libs/androidx_media_media/media-1.0.0.aar"
+  info_path = "libs/androidx_media_media/androidx_media_media.info"
+  deps = [
+    ":androidx_annotation_annotation_java",
+    ":androidx_core_core_java",
+    ":androidx_versionedparcelable_versionedparcelable_java",
+  ]
+
+  # Target has AIDL, but we do not support it yet: http://crbug.com/644439
+  ignore_aidl = true
+}
+
+# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
 android_aar_prebuilt("androidx_mediarouter_mediarouter_java") {
   aar_path = "libs/androidx_mediarouter_mediarouter/mediarouter-1.0.0.aar"
   info_path = "libs/androidx_mediarouter_mediarouter/androidx_mediarouter_mediarouter.info"
@@ -1833,24 +1847,6 @@
 }
 
 # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
-android_aar_prebuilt("androidx_media_media_java") {
-  aar_path = "libs/androidx_media_media/media-1.0.0.aar"
-  info_path = "libs/androidx_media_media/androidx_media_media.info"
-
-  # To remove visibility constraint, add this dependency to
-  # //third_party/android_deps/build.gradle.
-  visibility = [ ":*" ]
-  deps = [
-    ":androidx_annotation_annotation_java",
-    ":androidx_core_core_java",
-    ":androidx_versionedparcelable_versionedparcelable_java",
-  ]
-
-  # Target has AIDL, but we do not support it yet: http://crbug.com/644439
-  ignore_aidl = true
-}
-
-# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
 android_aar_prebuilt("androidx_palette_palette_java") {
   aar_path = "libs/androidx_palette_palette/palette-1.0.0.aar"
   info_path = "libs/androidx_palette_palette/androidx_palette_palette.info"
diff --git a/third_party/android_deps/build.gradle b/third_party/android_deps/build.gradle
index 725b84c..a040c46 100644
--- a/third_party/android_deps/build.gradle
+++ b/third_party/android_deps/build.gradle
@@ -44,6 +44,7 @@
     compile "androidx.interpolator:interpolator:${androidXSupportLibVersion}"
     compile "androidx.leanback:leanback-preference:${androidXSupportLibVersion}"
     compile "androidx.localbroadcastmanager:localbroadcastmanager:${androidXSupportLibVersion}"
+    compile "androidx.media:media:${androidXSupportLibVersion}"
     compile "androidx.mediarouter:mediarouter:${androidXSupportLibVersion}"
     compile "androidx.preference:preference:1.1.1"
     compile "androidx.recyclerview:recyclerview:${androidXSupportLibVersion}"
diff --git a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/LICENSE b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/LICENSE
index b233dc6a..70d6a70f 100644
--- a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/LICENSE
+++ b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/LICENSE
@@ -13,10 +13,9 @@
    (The text of this license also appears below.)  This applies to the
    checker-qual*.jar and all the files that appear in it:  every file in a
    qual/ directory, plus utility files FormatUtil.java,
-   I18nFormatUtil.java, NullnessUtil.java, Opt.java,
-   PurityUnqualified.java, RegexUtil.java, SignednessUtil.java, and
-   UnitsTools.java.  It also applies to other utility files
-   (SignednessUtilExtra.java) and to the cleanroom implementations of
+   I18nFormatUtil.java, NullnessUtil.java, Opt.java, PurityUnqualified.java,
+   RegexUtil.java, SignednessUtil.java, SignednessUtilExtra.java, and
+   UnitsTools.java.  It also applies to the cleanroom implementations of
    third-party annotations (in checker/src/testannotations/ and in
    framework/src/main/java/org/jmlspecs/).
 
diff --git a/third_party/android_deps/libs/org_checkerframework_checker_qual/LICENSE b/third_party/android_deps/libs/org_checkerframework_checker_qual/LICENSE
index b233dc6a..70d6a70f 100644
--- a/third_party/android_deps/libs/org_checkerframework_checker_qual/LICENSE
+++ b/third_party/android_deps/libs/org_checkerframework_checker_qual/LICENSE
@@ -13,10 +13,9 @@
    (The text of this license also appears below.)  This applies to the
    checker-qual*.jar and all the files that appear in it:  every file in a
    qual/ directory, plus utility files FormatUtil.java,
-   I18nFormatUtil.java, NullnessUtil.java, Opt.java,
-   PurityUnqualified.java, RegexUtil.java, SignednessUtil.java, and
-   UnitsTools.java.  It also applies to other utility files
-   (SignednessUtilExtra.java) and to the cleanroom implementations of
+   I18nFormatUtil.java, NullnessUtil.java, Opt.java, PurityUnqualified.java,
+   RegexUtil.java, SignednessUtil.java, SignednessUtilExtra.java, and
+   UnitsTools.java.  It also applies to the cleanroom implementations of
    third-party annotations (in checker/src/testannotations/ and in
    framework/src/main/java/org/jmlspecs/).
 
diff --git a/third_party/android_deps/libs/org_checkerframework_dataflow_shaded/LICENSE b/third_party/android_deps/libs/org_checkerframework_dataflow_shaded/LICENSE
index b233dc6a..70d6a70f 100644
--- a/third_party/android_deps/libs/org_checkerframework_dataflow_shaded/LICENSE
+++ b/third_party/android_deps/libs/org_checkerframework_dataflow_shaded/LICENSE
@@ -13,10 +13,9 @@
    (The text of this license also appears below.)  This applies to the
    checker-qual*.jar and all the files that appear in it:  every file in a
    qual/ directory, plus utility files FormatUtil.java,
-   I18nFormatUtil.java, NullnessUtil.java, Opt.java,
-   PurityUnqualified.java, RegexUtil.java, SignednessUtil.java, and
-   UnitsTools.java.  It also applies to other utility files
-   (SignednessUtilExtra.java) and to the cleanroom implementations of
+   I18nFormatUtil.java, NullnessUtil.java, Opt.java, PurityUnqualified.java,
+   RegexUtil.java, SignednessUtil.java, SignednessUtilExtra.java, and
+   UnitsTools.java.  It also applies to the cleanroom implementations of
    third-party annotations (in checker/src/testannotations/ and in
    framework/src/main/java/org/jmlspecs/).
 
diff --git a/third_party/android_sdk/androidx_browser/BUILD.gn b/third_party/android_sdk/androidx_browser/BUILD.gn
index 06d5b224..fd1042115d 100644
--- a/third_party/android_sdk/androidx_browser/BUILD.gn
+++ b/third_party/android_sdk/androidx_browser/BUILD.gn
@@ -45,6 +45,7 @@
     "//third_party/android_deps:androidx_collection_collection_java",
     "//third_party/android_deps:androidx_concurrent_concurrent_futures_java",
     "//third_party/android_deps:androidx_core_core_java",
+    "//third_party/android_deps:com_google_guava_listenablefuture_java",
   ]
   srcjar_deps = [ ":androidx_browser_service_aidl" ]
   chromium_code = false
diff --git a/third_party/blink/common/origin_trials/trial_token_validator.cc b/third_party/blink/common/origin_trials/trial_token_validator.cc
index 62f5323..99fafa3 100644
--- a/third_party/blink/common/origin_trials/trial_token_validator.cc
+++ b/third_party/blink/common/origin_trials/trial_token_validator.cc
@@ -54,10 +54,6 @@
       []() -> blink::OriginTrialPolicy* { return nullptr; }));
 }
 
-OriginTrialPolicy* TrialTokenValidator::Policy() {
-  return PolicyGetter().Run();
-}
-
 TrialTokenResult TrialTokenValidator::ValidateToken(
     base::StringPiece token,
     const url::Origin& origin,
@@ -70,9 +66,9 @@
     const url::Origin& origin,
     const url::Origin* third_party_origin,
     base::Time current_time) const {
-  OriginTrialPolicy* policy = Policy();
+  OriginTrialPolicy* policy = PolicyGetter().Run();
 
-  if (!policy->IsOriginTrialsSupported())
+  if (!policy || !policy->IsOriginTrialsSupported())
     return TrialTokenResult(OriginTrialTokenStatus::kNotSupported);
 
   std::vector<base::StringPiece> public_keys = policy->GetPublicKeys();
@@ -196,7 +192,7 @@
 
 // static
 bool TrialTokenValidator::IsTrialPossibleOnOrigin(const GURL& url) {
-  OriginTrialPolicy* policy = Policy();
+  OriginTrialPolicy* policy = PolicyGetter().Run();
   return policy && policy->IsOriginTrialsSupported() &&
          policy->IsOriginSecure(url);
 }
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 17da03a..cc091a4 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -82,6 +82,7 @@
   android_library("blink_headers_java") {
     deps = [
       "//services/network/public/mojom:mojom_java",
+      "//third_party/android_deps:androidx_annotation_annotation_java",
       "//third_party/android_deps:com_android_support_support_annotations_java",
     ]
     srcjar_deps = [ ":blink_headers_java_enums_srcjar" ]
diff --git a/third_party/blink/public/common/origin_trials/trial_token_validator.h b/third_party/blink/public/common/origin_trials/trial_token_validator.h
index aa27c9cf..b600d5f 100644
--- a/third_party/blink/public/common/origin_trials/trial_token_validator.h
+++ b/third_party/blink/public/common/origin_trials/trial_token_validator.h
@@ -97,7 +97,6 @@
   static void SetOriginTrialPolicyGetter(
       base::RepeatingCallback<OriginTrialPolicy*()> policy);
   static void ResetOriginTrialPolicyGetter();
-  static OriginTrialPolicy* Policy();
 
   static bool IsTrialPossibleOnOrigin(const GURL& url);
 };  // class TrialTokenValidator
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 23bb354..9ad400d 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -180,13 +180,6 @@
     "worker/worker_options.mojom",
   ]
 
-  if (is_android) {
-    # TODO(crbug.com/1080903): Figure out how to satisfy deps checker properly.
-    enable_bytecode_checks = false
-  } else {
-    sources += [ "serial/serial.mojom" ]
-  }
-
   if (is_win) {
     sources += [ "dwrite_font_proxy/dwrite_font_proxy.mojom" ]
   }
@@ -230,6 +223,16 @@
     "//url/mojom:url_mojom_origin",
   ]
 
+  if (is_android) {
+    # Direct deps (instead of transitive deps) are necessary for java targets.
+    public_deps += [
+      "//services/network/public/mojom:cookies_mojom",
+      "//ui/latency/mojom",
+    ]
+  } else {
+    sources += [ "serial/serial.mojom" ]
+  }
+
   if (is_mac) {
     public_deps += [
       "//ui/base/mojom",
@@ -343,9 +346,6 @@
     "webshare/webshare.mojom",
   ]
 
-  if (is_android) {
-    sources += [ "remote_objects/remote_objects.mojom" ]
-  }
   public_deps = [
     "//components/payments/mojom",
     "//components/schema_org/common:mojom",
@@ -356,6 +356,13 @@
     "//url/mojom:url_mojom_origin",
   ]
 
+  if (is_android) {
+    sources += [ "remote_objects/remote_objects.mojom" ]
+
+    # Direct deps (instead of transitive deps) are necessary for java targets.
+    public_deps += [ "//services/network/public/mojom:data_pipe_interfaces" ]
+  }
+
   # This Android unhandled-tap feature uses another mojo source, add it in.
   if (enable_unhandled_tap) {
     sources += [ "unhandled_tap_notifier/unhandled_tap_notifier.mojom" ]
@@ -443,8 +450,14 @@
   ]
 
   if (is_android) {
-    # TODO(crbug.com/1080903): Figure out how to satisfy deps checker properly.
-    enable_bytecode_checks = false
+    # Direct deps (instead of transitive deps) are necessary for java targets.
+    public_deps += [
+      "//services/data_decoder/public/mojom:mojom_resource_snapshot_for_web_bundle",
+      "//services/network/public/mojom:cookies_mojom",
+      "//third_party/blink/public/mojom/frame",
+      "//third_party/blink/public/mojom/tokens",
+      "//ui/events/mojom",
+    ]
   }
   if (is_mac) {
     public_deps += [
diff --git a/third_party/blink/public/mojom/screen_enumeration/screen_enumeration.mojom b/third_party/blink/public/mojom/screen_enumeration/screen_enumeration.mojom
index 6c6e7f9..92ba0dd 100644
--- a/third_party/blink/public/mojom/screen_enumeration/screen_enumeration.mojom
+++ b/third_party/blink/public/mojom/screen_enumeration/screen_enumeration.mojom
@@ -6,14 +6,27 @@
 
 import "ui/display/mojom/display.mojom";
 
-// This interface is implemented by the browser process to pass screen data to
-// window and worker processes.
+// A struct containing information about the set of connected displays.
+struct Displays {
+  array<display.mojom.Display> displays;   // The list of connected displays.
+  int64 internal_id;  // The internal display id or kInvalidDisplayId if none.
+  int64 primary_id;   // The primary display id or kInvalidDisplayId if none.
+};
+
+// An enum representing the presence of multiple displays, or an error state.
+enum MultipleDisplays {
+  kFalse,  // 0 or 1 displays are connected.
+  kTrue,   // 2 or more displays are connected.
+  kError,  // The display count is unavailable or access is denied.
+};
+
+// An interface enabling renderers to request information about screens
+// connected to the device from the browser process.
 interface ScreenEnumeration {
-  // If success is false, other returned values are meaningless. Otherwise,
-  // |displays| is the list of connected display devices; |internal_id| and
-  // |primary_id| are respectively the ids of the internal and primary displays.
-  GetDisplays() => (array<display.mojom.Display> displays,
-                    int64 internal_id,
-                    int64 primary_id,
-                    bool success);
+  // Returns information about the connected displays, or null if an error
+  // occurred (e.g. display information is unavailable or access is denied).
+  GetDisplays() => (Displays? result);
+
+  // Returns information about the presence of multiple displays.
+  HasMultipleDisplays() => (MultipleDisplays result);
 };
diff --git a/third_party/blink/public/web/modules/mediastream/web_media_stream_renderer_factory.h b/third_party/blink/public/web/modules/mediastream/web_media_stream_renderer_factory.h
index 2949c3f4..bfdf3e8 100644
--- a/third_party/blink/public/web/modules/mediastream/web_media_stream_renderer_factory.h
+++ b/third_party/blink/public/web/modules/mediastream/web_media_stream_renderer_factory.h
@@ -43,9 +43,6 @@
       base::RepeatingCallback<void()> on_render_error_callback) = 0;
 };
 
-BLINK_MODULES_EXPORT std::unique_ptr<WebMediaStreamRendererFactory>
-CreateWebMediaStreamRendererFactory();
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIASTREAM_WEB_MEDIA_STREAM_RENDERER_FACTORY_H_
diff --git a/third_party/blink/public/web/modules/mediastream/webmediaplayer_ms.h b/third_party/blink/public/web/modules/mediastream/webmediaplayer_ms.h
index 2b9976f..9644479d 100644
--- a/third_party/blink/public/web/modules/mediastream/webmediaplayer_ms.h
+++ b/third_party/blink/public/web/modules/mediastream/webmediaplayer_ms.h
@@ -83,7 +83,6 @@
       WebMediaPlayerClient* client,
       WebMediaPlayerDelegate* delegate,
       std::unique_ptr<media::MediaLog> media_log,
-      std::unique_ptr<WebMediaStreamRendererFactory> factory,
       scoped_refptr<base::SingleThreadTaskRunner> main_render_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
@@ -277,6 +276,8 @@
   // Helper method used for testing.
   void SetGpuMemoryBufferVideoForTesting(
       media::GpuMemoryBufferVideoFramePool* gpu_memory_buffer_pool);
+  void SetMediaStreamRendererFactoryForTesting(
+      std::unique_ptr<WebMediaStreamRendererFactory>);
 
   // Callback used to fulfill video.requestVideoFrameCallback() requests.
   void OnNewFramePresentedCallback();
diff --git a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
index 9f667e591..4b93ab7 100644
--- a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
+++ b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
@@ -248,8 +248,7 @@
 }
 
 ExecutionContext* ExecutionContextFromV8Wrappable(const DOMParser* parser) {
-  return parser->GetDocument() ? parser->GetDocument()->GetExecutionContext()
-                               : nullptr;
+  return parser->GetWindow();
 }
 
 v8::MaybeLocal<v8::Function> CreateNamedConstructorFunction(
diff --git a/third_party/blink/renderer/bindings/tests/idls/core/test_object.idl b/third_party/blink/renderer/bindings/tests/idls/core/test_object.idl
index 621a26fb..d2a82e9e 100644
--- a/third_party/blink/renderer/bindings/tests/idls/core/test_object.idl
+++ b/third_party/blink/renderer/bindings/tests/idls/core/test_object.idl
@@ -475,6 +475,7 @@
     // [HighEntropy=Direct]
     [HighEntropy=Direct,MeasureAs=TestAttributeHighEntropyUnsignedLong] readonly attribute unsigned long highEntropyDirectUnsignedLong;
     [HighEntropy=Direct,MeasureAs=TestAttributeHighEntropyDOMString] readonly attribute DOMString highEntropyDirectDomString;
+    [HighEntropy=Direct,MeasureAs=TestAttributeHighEntropyArrayDOMString] readonly attribute FrozenArray<DOMString> highEntropyDirectArrayDomString;
 
     // with [CEReactions] for not overloaded method
     [CEReactions] void ceReactionsNotOverloadedMethod(boolean arg);
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
index 1e95ac7..9cc830dc 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
@@ -4297,6 +4297,20 @@
   V8SetReturnValueString(info, cpp_value, info.GetIsolate());
 }
 
+static void HighEntropyDirectArrayDomStringAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Local<v8::Object> holder = info.Holder();
+
+  TestObject* impl = V8TestObject::ToImpl(holder);
+
+  ExecutionContext* execution_context = ExecutionContext::ForRelevantRealm(info);
+
+  Vector<String> cpp_value(impl->highEntropyDirectArrayDomString());
+
+  Dactyloscoper::RecordDirectSurface(execution_context, WebFeature::kTestAttributeHighEntropyArrayDOMString, cpp_value);
+
+  V8SetReturnValue(info, FreezeV8Object(ToV8(cpp_value, info.Holder(), info.GetIsolate()), info.GetIsolate()));
+}
+
 static void TestInterfaceAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) {
   v8::Local<v8::Object> holder = info.Holder();
 
@@ -11300,6 +11314,16 @@
   test_object_v8_internal::HighEntropyDirectDomStringAttributeGetter(info);
 }
 
+void V8TestObject::HighEntropyDirectArrayDomStringAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestObject_highEntropyDirectArrayDomString_Getter");
+
+  ExecutionContext* execution_context_for_measurement = CurrentExecutionContext(info.GetIsolate());
+  UseCounter::Count(execution_context_for_measurement, WebFeature::kTestAttributeHighEntropyArrayDOMString);
+  Dactyloscoper::Record(execution_context_for_measurement, WebFeature::kTestAttributeHighEntropyArrayDOMString);
+
+  test_object_v8_internal::HighEntropyDirectArrayDomStringAttributeGetter(info);
+}
+
 void V8TestObject::TestInterfaceAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
   RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestObject_testInterfaceAttribute_Getter");
 
@@ -13385,6 +13409,7 @@
       { "highEntropyReadonlyAttributeWithMeasureAs", V8TestObject::HighEntropyReadonlyAttributeWithMeasureAsAttributeGetterCallback, nullptr, static_cast<unsigned>(V8PrivateProperty::CachedAccessor::kNone), static_cast<v8::PropertyAttribute>(v8::ReadOnly), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAllWorlds },
       { "highEntropyDirectUnsignedLong", V8TestObject::HighEntropyDirectUnsignedLongAttributeGetterCallback, nullptr, static_cast<unsigned>(V8PrivateProperty::CachedAccessor::kNone), static_cast<v8::PropertyAttribute>(v8::ReadOnly), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAllWorlds },
       { "highEntropyDirectDomString", V8TestObject::HighEntropyDirectDomStringAttributeGetterCallback, nullptr, static_cast<unsigned>(V8PrivateProperty::CachedAccessor::kNone), static_cast<v8::PropertyAttribute>(v8::ReadOnly), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAllWorlds },
+      { "highEntropyDirectArrayDomString", V8TestObject::HighEntropyDirectArrayDomStringAttributeGetterCallback, nullptr, static_cast<unsigned>(V8PrivateProperty::CachedAccessor::kNone), static_cast<v8::PropertyAttribute>(v8::ReadOnly), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAllWorlds },
       { "testInterfaceAttribute", V8TestObject::TestInterfaceAttributeAttributeGetterCallback, V8TestObject::TestInterfaceAttributeAttributeSetterCallback, static_cast<unsigned>(V8PrivateProperty::CachedAccessor::kNone), static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAllWorlds },
       { "size", V8TestObject::SizeAttributeGetterCallback, nullptr, static_cast<unsigned>(V8PrivateProperty::CachedAccessor::kNone), static_cast<v8::PropertyAttribute>(v8::DontEnum | v8::ReadOnly), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kCheckAccess, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAllWorlds },
   };
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.h b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.h
index cd5346d1..a6c4b4fc 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.h
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.h
@@ -363,6 +363,7 @@
   CORE_EXPORT static void HighEntropyReadonlyAttributeWithMeasureAsAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void HighEntropyDirectUnsignedLongAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void HighEntropyDirectDomStringAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void HighEntropyDirectArrayDomStringAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void TestInterfaceAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void TestInterfaceAttributeAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   CORE_EXPORT static void SizeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
diff --git a/third_party/blink/renderer/core/css/css_font_face_src_value.cc b/third_party/blink/renderer/core/css/css_font_face_src_value.cc
index 928d946..f360b37 100644
--- a/third_party/blink/renderer/core/css/css_font_face_src_value.cc
+++ b/third_party/blink/renderer/core/css/css_font_face_src_value.cc
@@ -135,12 +135,11 @@
   DCHECK(context);
   DCHECK(context->Fetcher());
 
-  const String resource_url = context->CompleteURL(absolute_resource_);
+  const KURL url = context->CompleteURL(absolute_resource_);
   DCHECK_EQ(should_check_content_security_policy_,
             fetched_->GetResource()->Options().content_security_policy_option);
   context->Fetcher()->EmulateLoadStartedForInspector(
-      fetched_->GetResource(), KURL(resource_url),
-      mojom::RequestContextType::FONT,
+      fetched_->GetResource(), url, mojom::RequestContextType::FONT,
       network::mojom::RequestDestination::kFont,
       fetch_initiator_type_names::kCSS);
 }
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder.cc b/third_party/blink/renderer/core/css/resolver/style_builder.cc
index b39f767b..546f7a0 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder.cc
@@ -50,6 +50,7 @@
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
@@ -70,19 +71,27 @@
 
   CSSPropertyID id = property.PropertyID();
   bool is_inherited = property.IsInherited();
-  if (id != CSSPropertyID::kVariable && (value.IsVariableReferenceValue() ||
-                                         value.IsPendingSubstitutionValue())) {
-    bool omit_animation_tainted =
-        CSSAnimations::IsAnimationAffectingProperty(property);
-    const CSSValue* resolved_value =
-        CSSVariableResolver(state).ResolveVariableReferences(
-            id, value, omit_animation_tainted);
-    ApplyProperty(property, state, *resolved_value);
 
-    if (!state.Style()->HasVariableReferenceFromNonInheritedProperty() &&
-        !is_inherited)
-      state.Style()->SetHasVariableReferenceFromNonInheritedProperty();
-    return;
+  if (RuntimeEnabledFeatures::CSSCascadeEnabled()) {
+    // These values must be resolved by StyleCascade before application:
+    DCHECK(!value.IsVariableReferenceValue());
+    DCHECK(!value.IsPendingSubstitutionValue());
+  } else {
+    if (id != CSSPropertyID::kVariable &&
+        (value.IsVariableReferenceValue() ||
+         value.IsPendingSubstitutionValue())) {
+      bool omit_animation_tainted =
+          CSSAnimations::IsAnimationAffectingProperty(property);
+      const CSSValue* resolved_value =
+          CSSVariableResolver(state).ResolveVariableReferences(
+              id, value, omit_animation_tainted);
+      ApplyProperty(property, state, *resolved_value);
+
+      if (!state.Style()->HasVariableReferenceFromNonInheritedProperty() &&
+          !is_inherited)
+        state.Style()->SetHasVariableReferenceFromNonInheritedProperty();
+      return;
+    }
   }
 
   DCHECK(!property.IsShorthand())
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index c0ade72..76ba181 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -670,8 +670,7 @@
   Document* new_document = MakeGarbageCollected<Document>(
       DocumentInit::Create()
           .WithExecutionContext(document.GetExecutionContext())
-          .WithURL(BlankURL())
-          .WithOwnerDocument(&document));
+          .WithURL(BlankURL()));
   new_document->SetContextFeatures(document.GetContextFeatures());
   return new_document;
 }
@@ -694,6 +693,8 @@
       context_features_(ContextFeatures::DefaultSwitch()),
       http_refresh_scheduler_(MakeGarbageCollected<HttpRefreshScheduler>(this)),
       well_formed_(false),
+      cookie_url_(dom_window_ ? initializer.GetCookieUrl()
+                              : KURL(g_empty_string)),
       printing_(kNotPrinting),
       is_painting_preview_(false),
       compatibility_mode_(kNoQuirksMode),
@@ -840,9 +841,6 @@
     SetBaseURLOverride(initializer.GetWebBundleClaimedUrl());
   }
 
-  cookie_url_ = initializer.HasSecurityContext() ? initializer.GetCookieUrl()
-                                                 : KURL(g_empty_string);
-
   is_vertical_scroll_enforced_ =
       GetFrame() && !GetFrame()->IsMainFrame() &&
       RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled() &&
@@ -4897,7 +4895,6 @@
 Document* Document::CloneDocumentWithoutChildren() const {
   DocumentInit init = DocumentInit::Create()
                           .WithExecutionContext(execution_context_.Get())
-                          .WithOwnerDocument(const_cast<Document*>(this))
                           .WithURL(Url());
   if (IsA<XMLDocument>(this)) {
     if (IsXHTMLDocument())
diff --git a/third_party/blink/renderer/core/dom/document_init.cc b/third_party/blink/renderer/core/dom/document_init.cc
index 8f240cb..d5274be 100644
--- a/third_party/blink/renderer/core/dom/document_init.cc
+++ b/third_party/blink/renderer/core/dom/document_init.cc
@@ -117,7 +117,8 @@
   return nullptr;
 }
 
-DocumentInit& DocumentInit::WithDocumentLoader(DocumentLoader* loader) {
+DocumentInit& DocumentInit::WithDocumentLoader(DocumentLoader* loader,
+                                               Document* owner_document) {
   DCHECK(!document_loader_);
   DCHECK(!execution_context_);
   DCHECK(!imports_controller_);
@@ -127,6 +128,7 @@
   DCHECK(loader);
   document_loader_ = loader;
   parent_document_ = ParentDocument(document_loader_);
+  owner_document_ = owner_document;
   return *this;
 }
 
@@ -220,11 +222,6 @@
   mime_type_ = mime_type;
   type_ = ComputeDocumentType(GetFrame(), Url(), mime_type_,
                               &is_for_external_handler_);
-  if (type_ == Type::kPlugin) {
-    plugin_background_color_ =
-        GetPluginData(GetFrame(), Url())
-            ->PluginBackgroundColorForMimeType(mime_type_);
-  }
   return *this;
 }
 
@@ -249,12 +246,6 @@
   return owner_document_ ? owner_document_->CookieURL() : url_;
 }
 
-DocumentInit& DocumentInit::WithOwnerDocument(Document* owner_document) {
-  DCHECK(!owner_document_);
-  owner_document_ = owner_document;
-  return *this;
-}
-
 DocumentInit& DocumentInit::WithSrcdocDocument(bool is_srcdoc_document) {
   is_srcdoc_document_ = is_srcdoc_document;
   return *this;
diff --git a/third_party/blink/renderer/core/dom/document_init.h b/third_party/blink/renderer/core/dom/document_init.h
index 4ce78e9f..4a83529 100644
--- a/third_party/blink/renderer/core/dom/document_init.h
+++ b/third_party/blink/renderer/core/dom/document_init.h
@@ -100,11 +100,10 @@
     return imports_controller_;
   }
 
-  bool HasSecurityContext() const { return TreeRootDocumentLoader(); }
   bool IsSrcdocDocument() const;
   bool ShouldSetURL() const;
 
-  DocumentInit& WithDocumentLoader(DocumentLoader*);
+  DocumentInit& WithDocumentLoader(DocumentLoader*, Document* owner_document);
   LocalFrame* GetFrame() const;
   UseCounter* GetUseCounter() const;
 
@@ -122,7 +121,6 @@
   Type GetType() const { return type_; }
   const String& GetMimeType() const { return mime_type_; }
   bool IsForExternalHandler() const { return is_for_external_handler_; }
-  Color GetPluginBackgroundColor() const { return plugin_background_color_; }
 
   // Used when creating Documents not attached to a window.
   DocumentInit& WithExecutionContext(ExecutionContext*);
@@ -133,9 +131,6 @@
 
   const KURL& GetCookieUrl() const;
 
-  // Specifies the Document to inherit security configurations from.
-  DocumentInit& WithOwnerDocument(Document*);
-
   DocumentInit& WithSrcdocDocument(bool is_srcdoc_document);
 
   DocumentInit& WithRegistrationContext(V0CustomElementRegistrationContext*);
@@ -189,7 +184,6 @@
   KURL web_bundle_claimed_url_;
 
   bool is_for_external_handler_ = false;
-  Color plugin_background_color_;
 
 #if DCHECK_IS_ON()
   bool for_test_ = false;
diff --git a/third_party/blink/renderer/core/dom/dom_implementation.cc b/third_party/blink/renderer/core/dom/dom_implementation.cc
index 488f4b0..3df0b801 100644
--- a/third_party/blink/renderer/core/dom/dom_implementation.cc
+++ b/third_party/blink/renderer/core/dom/dom_implementation.cc
@@ -73,14 +73,12 @@
     const AtomicString& qualified_name,
     DocumentType* doctype,
     ExceptionState& exception_state) {
-  if (!document_->GetExecutionContext())
+  ExecutionContext* context = document_->GetExecutionContext();
+  if (!context)
     return nullptr;
 
   XMLDocument* doc = nullptr;
-  DocumentInit init =
-      DocumentInit::Create()
-          .WithExecutionContext(document_->GetExecutionContext())
-          .WithOwnerDocument(document_);
+  DocumentInit init = DocumentInit::Create().WithExecutionContext(context);
   if (namespace_uri == svg_names::kNamespaceURI) {
     doc = XMLDocument::CreateSVG(init);
   } else if (namespace_uri == html_names::xhtmlNamespaceURI) {
@@ -114,7 +112,6 @@
   DocumentInit init =
       DocumentInit::Create()
           .WithExecutionContext(document_->GetExecutionContext())
-          .WithOwnerDocument(document_)
           .WithRegistrationContext(document_->RegistrationContext());
   auto* d = MakeGarbageCollected<HTMLDocument>(init);
   d->open();
diff --git a/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc b/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
index f3b8c9c..8c623f6 100644
--- a/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
@@ -164,7 +164,7 @@
       first_letter_text_layout_object =
           first_letter_text_layout_object->NextSibling();
     } else if (first_letter_text_layout_object->IsAtomicInlineLevel() ||
-               first_letter_text_layout_object->IsLayoutButton() ||
+               first_letter_text_layout_object->IsButtonOrNGButton() ||
                IsMenuList(first_letter_text_layout_object)) {
       return nullptr;
     } else if (first_letter_text_layout_object
diff --git a/third_party/blink/renderer/core/dom/text.cc b/third_party/blink/renderer/core/dom/text.cc
index 2816ab0..beec5e5 100644
--- a/third_party/blink/renderer/core/dom/text.cc
+++ b/third_party/blink/renderer/core/dom/text.cc
@@ -254,7 +254,7 @@
   const LayoutObject& parent = *context.parent;
   // <button> and <fieldset> should allow whitespace even though
   // LayoutFlexibleBox doesn't.
-  if (parent.IsLayoutButton() || parent.IsFieldset())
+  if (parent.IsButtonOrNGButton() || parent.IsFieldset())
     return true;
 
   if (parent.IsTable() || parent.IsTableRow() || parent.IsTableSection() ||
diff --git a/third_party/blink/renderer/core/frame/dactyloscoper.cc b/third_party/blink/renderer/core/frame/dactyloscoper.cc
index 1fc5af7..dc27344 100644
--- a/third_party/blink/renderer/core/frame/dactyloscoper.cc
+++ b/third_party/blink/renderer/core/frame/dactyloscoper.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/frame/dactyloscoper.h"
 
 #include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
+#include "third_party/blink/public/common/privacy_budget/identifiable_token_builder.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -43,19 +44,34 @@
       .Record(document->UkmRecorder());
 }
 
+// The anonymous namespace ensures this StringToBytes has internal linkage,
+// in case someone defines another blink::StringToBytes elsewhere.
+namespace {
+
+base::span<const uint8_t> StringToBytes(String str) {
+  return str.Is8Bit() ? str.Span8() : as_bytes(str.Span16());
+}
+
+}  // namespace
+
 void Dactyloscoper::RecordDirectSurface(ExecutionContext* context,
                                         WebFeature feature,
                                         String str) {
-  auto* window = DynamicTo<LocalDOMWindow>(context);
-  if (!window)
-    return;
   if (str.IsEmpty())
     return;
-  Document* document = window->document();
-  IdentifiabilityMetricBuilder(document->UkmSourceID())
-      .SetWebfeature(feature,
-                     str.Is8Bit() ? str.Span8() : as_bytes(str.Span16()))
-      .Record(document->UkmRecorder());
+  Dactyloscoper::RecordDirectSurface(context, feature, StringToBytes(str));
+}
+
+void Dactyloscoper::RecordDirectSurface(ExecutionContext* context,
+                                        WebFeature feature,
+                                        Vector<String> strs) {
+  if (strs.IsEmpty())
+    return;
+  IdentifiableTokenBuilder builder;
+  for (const auto& str : strs) {
+    builder.AddAtomic(StringToBytes(str));
+  }
+  Dactyloscoper::RecordDirectSurface(context, feature, builder.GetToken());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/dactyloscoper.h b/third_party/blink/renderer/core/frame/dactyloscoper.h
index 4c25ae7..ef48c84 100644
--- a/third_party/blink/renderer/core/frame/dactyloscoper.h
+++ b/third_party/blink/renderer/core/frame/dactyloscoper.h
@@ -30,6 +30,9 @@
                                   WebFeature,
                                   IdentifiableToken);
   static void RecordDirectSurface(ExecutionContext*, WebFeature, String);
+  static void RecordDirectSurface(ExecutionContext*,
+                                  WebFeature,
+                                  Vector<String>);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Dactyloscoper);
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 971224f..8a85b12b 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1826,7 +1826,7 @@
 
   DomWindow()->InstallNewDocument(
       DocumentInit::Create()
-          .WithDocumentLoader(loader_.GetDocumentLoader())
+          .WithDocumentLoader(loader_.GetDocumentLoader(), nullptr)
           .WithTypeFrom(mime_type));
   loader_.StateMachine()->AdvanceTo(
       FrameLoaderStateMachine::kCommittedFirstRealLoad);
diff --git a/third_party/blink/renderer/core/frame/navigator_language.idl b/third_party/blink/renderer/core/frame/navigator_language.idl
index c766f62..456fc0e 100644
--- a/third_party/blink/renderer/core/frame/navigator_language.idl
+++ b/third_party/blink/renderer/core/frame/navigator_language.idl
@@ -6,5 +6,5 @@
 
 interface mixin NavigatorLanguage {
     [HighEntropy=Direct, MeasureAs=NavigatorLanguage] readonly attribute DOMString language;
-    [CachedAttribute=IsLanguagesDirty, HighEntropy, MeasureAs=NavigatorLanguages] readonly attribute FrozenArray<DOMString> languages;
+    [CachedAttribute=IsLanguagesDirty, HighEntropy=Direct, MeasureAs=NavigatorLanguages] readonly attribute FrozenArray<DOMString> languages;
 };
diff --git a/third_party/blink/renderer/core/html/html_document.cc b/third_party/blink/renderer/core/html/html_document.cc
index c3dd77c6..19ddc7e 100644
--- a/third_party/blink/renderer/core/html/html_document.cc
+++ b/third_party/blink/renderer/core/html/html_document.cc
@@ -82,7 +82,6 @@
   return MakeGarbageCollected<HTMLDocument>(
       DocumentInit::Create()
           .WithExecutionContext(GetExecutionContext())
-          .WithOwnerDocument(const_cast<HTMLDocument*>(this))
           .WithURL(Url())
           .WithRegistrationContext(RegistrationContext()));
 }
diff --git a/third_party/blink/renderer/core/html/plugin_document.cc b/third_party/blink/renderer/core/html/plugin_document.cc
index 17d9289..1b8fe90 100644
--- a/third_party/blink/renderer/core/html/plugin_document.cc
+++ b/third_party/blink/renderer/core/html/plugin_document.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/layout/layout_embedded_object.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/loader/frame_loader.h"
+#include "third_party/blink/renderer/core/page/plugin_data.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
@@ -182,14 +183,14 @@
 
 PluginDocument::PluginDocument(const DocumentInit& initializer)
     : HTMLDocument(initializer, kPluginDocumentClass),
-      background_color_(initializer.GetPluginBackgroundColor()) {
+      background_color_(
+          GetFrame()->GetPluginData()->PluginBackgroundColorForMimeType(
+              initializer.GetMimeType())) {
   SetCompatibilityMode(kQuirksMode);
   LockCompatibilityMode();
-  if (GetExecutionContext()) {
-    GetExecutionContext()->GetScheduler()->RegisterStickyFeature(
-        SchedulingPolicy::Feature::kContainsPlugins,
-        {SchedulingPolicy::RecordMetricsForBackForwardCache()});
-  }
+  GetExecutionContext()->GetScheduler()->RegisterStickyFeature(
+      SchedulingPolicy::Feature::kContainsPlugins,
+      {SchedulingPolicy::RecordMetricsForBackForwardCache()});
 }
 
 DocumentParser* PluginDocument::CreateParser() {
diff --git a/third_party/blink/renderer/core/layout/generated_children.h b/third_party/blink/renderer/core/layout/generated_children.h
index 6ed0c90..b06822b 100644
--- a/third_party/blink/renderer/core/layout/generated_children.h
+++ b/third_party/blink/renderer/core/layout/generated_children.h
@@ -24,7 +24,7 @@
   // Input elements can't have generated children, but button elements can.
   // We'll write the code assuming any other button types that might emerge in
   // the future can also have children.
-  if (layout_object.IsLayoutButton())
+  if (layout_object.IsButtonOrNGButton())
     return !IsA<HTMLInputElement>(*layout_object.GetNode());
 
   return layout_object.CanHaveChildren();
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 3059723f..ce750e3 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -2238,7 +2238,7 @@
   if (UNLIKELY(input)) {
     // As for LayoutButton, ControlClip is to for not BUTTONs but INPUT
     // buttons for IE/Firefox compatibility.
-    if (IsTextField() || IsLayoutButton()) {
+    if (IsTextField() || IsButtonOrNGButton()) {
       DCHECK(HasControlClip());
       PhysicalRect control_clip = PhysicalPaddingBoxRect();
       control_clip.Move(location);
@@ -2258,7 +2258,7 @@
 
 bool LayoutBox::HasControlClip() const {
   return UNLIKELY(IsTextField() || IsFileUploadControl() || IsMenuList(this) ||
-                  (IsLayoutButton() && IsA<HTMLInputElement>(GetNode())));
+                  (IsButtonOrNGButton() && IsA<HTMLInputElement>(GetNode())));
 }
 
 void LayoutBox::ExcludeScrollbars(
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index b0cba3c..bff3a4e 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -716,7 +716,10 @@
   }
   bool IsProgress() const { return IsOfType(kLayoutObjectProgress); }
   bool IsQuote() const { return IsOfType(kLayoutObjectQuote); }
-  bool IsLayoutButton() const { return IsOfType(kLayoutObjectLayoutButton); }
+  bool IsButtonOrNGButton() const {
+    return IsOfType(kLayoutObjectLayoutButton) ||
+           IsOfType(kLayoutObjectNGButton);
+  }
   bool IsLayoutNGButton() const { return IsOfType(kLayoutObjectNGButton); }
   bool IsLayoutNGCustom() const {
     return IsOfType(kLayoutObjectLayoutNGCustom);
@@ -1293,7 +1296,7 @@
            SpannerPlaceholder();
   }
 
-  // We include isLayoutButton() in this check, because buttons are
+  // We include IsButtonOrNGButton() in this check, because buttons are
   // implemented using flex box but should still support things like
   // first-line, first-letter and text-overflow.
   // The flex box and grid specs require that flex box and grid do not
@@ -1304,7 +1307,7 @@
   // instead of flex box. crbug.com/226252.
   bool BehavesLikeBlockContainer() const {
     return (IsLayoutBlockFlow() && StyleRef().IsDisplayBlockContainer()) ||
-           IsLayoutButton();
+           IsButtonOrNGButton();
   }
 
   // May be optionally passed to container() and various other similar methods
diff --git a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
index f8acd6f..af4973b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
@@ -338,6 +338,25 @@
           (style.LogicalTop().IsAuto() || style.LogicalBottom().IsAuto()));
 }
 
+bool IsInlineSizeComputableFromBlockSize(const ComputedStyle& style) {
+  DCHECK(style.HasOutOfFlowPosition());
+  if (!style.AspectRatio())
+    return false;
+  // An explicit block size should take precedence over specified insets.
+  bool have_inline_size =
+      style.LogicalWidth().IsFixed() || style.LogicalWidth().IsPercentOrCalc();
+  bool have_block_size = style.LogicalHeight().IsFixed() ||
+                         style.LogicalHeight().IsPercentOrCalc();
+  if (have_inline_size)
+    return false;
+  if (have_block_size)
+    return true;
+  // If we have block insets but no inline insets, we compute based on the
+  // insets.
+  return !AbsoluteNeedsChildBlockSize(style) &&
+         AbsoluteNeedsChildInlineSize(style);
+}
+
 base::Optional<LayoutUnit> ComputeAbsoluteDialogYPosition(
     const LayoutObject& dialog,
     LayoutUnit height) {
@@ -406,6 +425,9 @@
                                           min_max_sizes, style.LogicalWidth());
   } else if (replaced_size.has_value()) {
     inline_size = replaced_size->inline_size;
+  } else if (IsInlineSizeComputableFromBlockSize(style)) {
+    DCHECK(min_max_sizes.has_value());
+    inline_size = min_max_sizes->min_size;
   }
 
   LayoutUnit min_inline_size = ResolveMinInlineLength(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h
index 5c2f369..ee4235d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h
@@ -55,6 +55,10 @@
 // block-size.
 CORE_EXPORT bool AbsoluteNeedsChildBlockSize(const ComputedStyle&);
 
+// Returns true if the inline size can be computed from an aspect ratio and
+// the block size.
+bool IsInlineSizeComputableFromBlockSize(const ComputedStyle& style);
+
 // Computes part of the absolute position which depends on the child's
 // inline-size.
 // |replaced_size| should be set if and only if element is replaced element.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc
index dac7b8f7..07dad74 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc
@@ -29,6 +29,7 @@
 
   void SetUp() override {
     style_ = ComputedStyle::Create();
+    style_->SetPosition(EPosition::kAbsolute);
     // If not set, border widths will always be 0.
     style_->SetBorderLeftStyle(EBorderStyle::kSolid);
     style_->SetBorderRightStyle(EBorderStyle::kSolid);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index f06a0e0..492da17 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -730,12 +730,19 @@
     constraint_space = &zero_constraint_space;
 
   if (Style().AspectRatio() && input.type == MinMaxSizesType::kContent) {
+    LayoutUnit block_size(kIndefiniteSize);
+    if (IsOutOfFlowPositioned()) {
+      // For out-of-flow, the input percentage block size is actually our
+      // block size. We should use that for aspect-ratio purposes if known.
+      block_size = input.percentage_resolution_block_size;
+    }
+
     NGFragmentGeometry fragment_geometry =
         CalculateInitialMinMaxFragmentGeometry(*constraint_space, *this);
     NGBoxStrut border_padding =
         fragment_geometry.border + fragment_geometry.padding;
     LayoutUnit size_from_ar = ComputeInlineSizeFromAspectRatio(
-        *constraint_space, Style(), border_padding);
+        *constraint_space, Style(), border_padding, block_size);
     if (size_from_ar != kIndefiniteSize) {
       return {{size_from_ar, size_from_ar},
               Style().LogicalHeight().IsPercentOrCalc()};
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
index f7103e2..890a7b0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
@@ -225,8 +225,6 @@
   }
 }
 
-namespace {
-
 LayoutUnit InlineSizeFromAspectRatio(const NGBoxStrut& border_padding,
                                      const LogicalSize& aspect_ratio,
                                      EBoxSizing box_sizing,
@@ -251,6 +249,8 @@
          border_padding.BlockSum();
 }
 
+namespace {
+
 template <typename MinMaxSizesFunc>
 MinMaxSizesResult ComputeMinAndMaxContentContributionInternal(
     WritingMode parent_writing_mode,
@@ -401,18 +401,21 @@
 
 LayoutUnit ComputeInlineSizeFromAspectRatio(const NGConstraintSpace& space,
                                             const ComputedStyle& style,
-                                            const NGBoxStrut& border_padding) {
-  if (UNLIKELY(style.LogicalAspectRatio() && !style.LogicalHeight().IsAuto())) {
-    // Check if we can get an inline size using the aspect ratio
-    LayoutUnit block_size = ComputeBlockSizeForFragment(
-        space, style, border_padding, kIndefiniteSize, base::nullopt);
-    if (block_size != kIndefiniteSize) {
-      return InlineSizeFromAspectRatio(border_padding,
-                                       *style.LogicalAspectRatio(),
-                                       style.BoxSizing(), block_size);
-    }
+                                            const NGBoxStrut& border_padding,
+                                            LayoutUnit block_size) {
+  if (LIKELY(!style.AspectRatio()))
+    return kIndefiniteSize;
+
+  if (!style.LogicalHeight().IsAuto() && block_size == kIndefiniteSize) {
+    DCHECK(!style.HasOutOfFlowPosition()) << "OOF should pass in a block size";
+    block_size = ComputeBlockSizeForFragment(space, style, border_padding,
+                                             kIndefiniteSize, base::nullopt);
   }
-  return kIndefiniteSize;
+  if (block_size == kIndefiniteSize)
+    return kIndefiniteSize;
+  // Check if we can get an inline size using the aspect ratio.
+  return InlineSizeFromAspectRatio(border_padding, *style.LogicalAspectRatio(),
+                                   style.BoxSizing(), block_size);
 }
 
 LayoutUnit ComputeInlineSizeForFragment(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
index 53a4f31..74c817fd 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
@@ -41,6 +41,16 @@
          style.LogicalMaxWidth().IsIntrinsic();
 }
 
+LayoutUnit InlineSizeFromAspectRatio(const NGBoxStrut& border_padding,
+                                     const LogicalSize& aspect_ratio,
+                                     EBoxSizing box_sizing,
+                                     LayoutUnit block_size);
+
+LayoutUnit BlockSizeFromAspectRatio(const NGBoxStrut& border_padding,
+                                    const LogicalSize& aspect_ratio,
+                                    EBoxSizing box_sizing,
+                                    LayoutUnit inline_size);
+
 // Returns if the given |Length| is unresolvable, e.g. the length is %-based
 // during the intrinsic phase. For block lengths we also consider 'auto',
 // 'min-content', 'max-content', 'fit-content' and 'none' (for max-block-size)
@@ -311,9 +321,13 @@
 // Tries to compute the inline size of a node from its block size and
 // aspect ratio. If there is no aspect ratio or the block size is indefinite,
 // returns kIndefiniteSize.
-LayoutUnit ComputeInlineSizeFromAspectRatio(const NGConstraintSpace&,
-                                            const ComputedStyle&,
-                                            const NGBoxStrut& border_padding);
+// block_size can be specified to base the calculation off of that size
+// instead of calculating it.
+LayoutUnit ComputeInlineSizeFromAspectRatio(
+    const NGConstraintSpace&,
+    const ComputedStyle&,
+    const NGBoxStrut& border_padding,
+    LayoutUnit block_size = kIndefiniteSize);
 
 // Returns inline size of the node's border box by resolving the computed value
 // in style.logicalWidth (Length) to a layout unit, adding border and padding,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index 078ee4f..bf0a8e5 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -730,8 +730,12 @@
   bool absolute_needs_child_block_size =
       AbsoluteNeedsChildBlockSize(candidate_style);
 
+  // We also include items with aspect ratio here, because if the inline size
+  // is auto and we have a definite block size, we want to use that for the
+  // inline size calculation.
   if (AbsoluteNeedsChildInlineSize(candidate_style) ||
-      NeedMinMaxSize(candidate_style) || should_be_considered_as_replaced) {
+      NeedMinMaxSize(candidate_style) || should_be_considered_as_replaced ||
+      IsInlineSizeComputableFromBlockSize(candidate_style)) {
     MinMaxSizesInput input(kIndefiniteSize, MinMaxSizesType::kContent);
     if (is_replaced) {
       input.percentage_resolution_block_size =
@@ -753,14 +757,13 @@
   }
 
   base::Optional<LogicalSize> replaced_size;
-  base::Optional<LogicalSize> replaced_aspect_ratio;
+  base::Optional<LogicalSize> aspect_ratio;
   bool has_aspect_ratio_without_intrinsic_size = false;
   if (is_replaced) {
     ComputeReplacedSize(node, candidate_constraint_space, min_max_sizes,
-                        &replaced_size, &replaced_aspect_ratio);
-    has_aspect_ratio_without_intrinsic_size = !replaced_size &&
-                                              replaced_aspect_ratio &&
-                                              !replaced_aspect_ratio->IsEmpty();
+                        &replaced_size, &aspect_ratio);
+    has_aspect_ratio_without_intrinsic_size =
+        !replaced_size && aspect_ratio && !aspect_ratio->IsEmpty();
     // If we only have aspect ratio, and no replaced size, intrinsic size
     // defaults to 300x150. min_max_sizes gets computed from the intrinsic size.
     // We reset the min_max_sizes because spec says that OOF-positioned size
@@ -768,6 +771,9 @@
     // https://www.w3.org/TR/CSS22/visudet.html#inline-replaced-width
     if (has_aspect_ratio_without_intrinsic_size)
       min_max_sizes = MinMaxSizes{LayoutUnit(), LayoutUnit::NearlyMax()};
+  } else if (candidate_style.AspectRatio()) {
+    has_aspect_ratio_without_intrinsic_size = true;
+    aspect_ratio = node.GetAspectRatio();
   } else if (should_be_considered_as_replaced) {
     replaced_size =
         LogicalSize{min_max_sizes->ShrinkToFit(
@@ -789,12 +795,15 @@
   // inline size and aspect ratio.
   // https://www.w3.org/TR/css-sizing-3/#intrinsic-sizes
   if (has_aspect_ratio_without_intrinsic_size) {
+    // If this came from an aspect-ratio property, we need to respect
+    // box-sizing.
+    EBoxSizing sizing = candidate_style.AspectRatio()
+                            ? candidate_style.BoxSizing()
+                            : EBoxSizing::kContentBox;
     replaced_size = LogicalSize(
         node_dimensions.size.inline_size,
-        (replaced_aspect_ratio->block_size *
-         ((node_dimensions.size.inline_size - border_padding.InlineSum()) /
-          replaced_aspect_ratio->inline_size)) +
-            border_padding.BlockSum());
+        BlockSizeFromAspectRatio(border_padding, *aspect_ratio, sizing,
+                                 node_dimensions.size.inline_size));
   }
   if (absolute_needs_child_block_size) {
     DCHECK(!has_computed_block_dimensions);
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell_legacy.cc b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell_legacy.cc
index 4e5d2eb..ea5f315 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell_legacy.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell_legacy.cc
@@ -21,7 +21,7 @@
 void LayoutNGTableCellLegacy::UpdateBlockLayout(bool relayout_children) {
   LayoutAnalyzer::BlockScope analyzer(*this);
 
-  SetOverrideLogicalWidth(LogicalWidth());
+  SetOverrideLogicalWidth(LogicalWidth().ClampNegativeToZero());
   UpdateInFlowBlockLayout();
 }
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc
index 0b8eb05..d365a71 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc
@@ -49,6 +49,22 @@
   LayoutBlockFlow::WillBeDestroyed();
 }
 
+void LayoutSVGBlock::InsertedIntoTree() {
+  LayoutBlockFlow::InsertedIntoTree();
+  if (CompositingReasonFinder::DirectReasonsForSVGChildPaintProperties(*this) !=
+      CompositingReason::kNone) {
+    SVGLayoutSupport::NotifySVGRootOfChangedCompositingReasons(this);
+  }
+}
+
+void LayoutSVGBlock::WillBeRemovedFromTree() {
+  LayoutBlockFlow::WillBeRemovedFromTree();
+  if (CompositingReasonFinder::DirectReasonsForSVGChildPaintProperties(*this) !=
+      CompositingReason::kNone) {
+    SVGLayoutSupport::NotifySVGRootOfChangedCompositingReasons(this);
+  }
+}
+
 void LayoutSVGBlock::UpdateFromStyle() {
   LayoutBlockFlow::UpdateFromStyle();
   SetFloating(false);
@@ -109,6 +125,9 @@
     }
   }
 
+  if (diff.CompositingReasonsChanged())
+    SVGLayoutSupport::NotifySVGRootOfChangedCompositingReasons(this);
+
   LayoutBlock::StyleDidChange(diff, old_style);
   SVGResources::UpdateClipPathFilterMask(*GetElement(), old_style, StyleRef());
   SVGResourcesCache::ClientStyleChanged(*this, diff, StyleRef());
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_block.h b/third_party/blink/renderer/core/layout/svg/layout_svg_block.h
index d13579e..7e3e67c 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_block.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_block.h
@@ -58,6 +58,9 @@
 
  protected:
   void WillBeDestroyed() override;
+  void InsertedIntoTree() override;
+  void WillBeRemovedFromTree() override;
+
   bool MapToVisualRectInAncestorSpaceInternal(
       const LayoutBoxModelObject* ancestor,
       TransformState&,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
index 9ce624f..63d19b6 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
@@ -174,9 +174,18 @@
 }
 
 void LayoutSVGContainer::UpdateCachedBoundaries() {
+  auto old_object_bounding_box = object_bounding_box_;
+
   SVGLayoutSupport::ComputeContainerBoundingBoxes(
       this, object_bounding_box_, object_bounding_box_valid_,
       stroke_bounding_box_, local_visual_rect_);
+
+  // Change of object_bounding_box_ may change reference box of resource
+  // effects.
+  if (old_object_bounding_box != object_bounding_box_ &&
+      !IsSVGHiddenContainer() &&
+      SVGResourcesCache::CachedResourcesForLayoutObject(*this))
+    SetShouldDoFullPaintInvalidation();
 }
 
 bool LayoutSVGContainer::NodeAtPoint(HitTestResult& result,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
index 04b832c..92aa45c 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
@@ -127,6 +127,9 @@
   if (diff.NeedsFullLayout())
     SetNeedsBoundariesUpdate();
 
+  if (diff.CompositingReasonsChanged())
+    SVGLayoutSupport::NotifySVGRootOfChangedCompositingReasons(this);
+
   LayoutInline::StyleDidChange(diff, old_style);
   SVGResources::UpdateClipPathFilterMask(To<SVGElement>(*GetNode()), old_style,
                                          StyleRef());
@@ -149,4 +152,20 @@
   LayoutInline::RemoveChild(child);
 }
 
+void LayoutSVGInline::InsertedIntoTree() {
+  LayoutInline::InsertedIntoTree();
+  if (CompositingReasonFinder::DirectReasonsForSVGChildPaintProperties(*this) !=
+      CompositingReason::kNone) {
+    SVGLayoutSupport::NotifySVGRootOfChangedCompositingReasons(this);
+  }
+}
+
+void LayoutSVGInline::WillBeRemovedFromTree() {
+  LayoutInline::WillBeRemovedFromTree();
+  if (CompositingReasonFinder::DirectReasonsForSVGChildPaintProperties(*this) !=
+      CompositingReason::kNone) {
+    SVGLayoutSupport::NotifySVGRootOfChangedCompositingReasons(this);
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_inline.h b/third_party/blink/renderer/core/layout/svg/layout_svg_inline.h
index 19b362d2..d4294b6 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_inline.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_inline.h
@@ -62,6 +62,9 @@
   void AddChild(LayoutObject* child,
                 LayoutObject* before_child = nullptr) final;
   void RemoveChild(LayoutObject*) final;
+
+  void InsertedIntoTree() override;
+  void WillBeRemovedFromTree() override;
 };
 
 DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutSVGInline, IsSVGInline());
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc
index 48460002..4b06adc8 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc
@@ -148,9 +148,28 @@
       SetNeedsPaintPropertyUpdate();
   }
 
+  if (diff.CompositingReasonsChanged())
+    SVGLayoutSupport::NotifySVGRootOfChangedCompositingReasons(this);
+
   LayoutObject::StyleDidChange(diff, old_style);
   SVGResources::UpdateClipPathFilterMask(*GetElement(), old_style, StyleRef());
   SVGResourcesCache::ClientStyleChanged(*this, diff, StyleRef());
 }
 
+void LayoutSVGModelObject::InsertedIntoTree() {
+  LayoutObject::InsertedIntoTree();
+  if (CompositingReasonFinder::DirectReasonsForSVGChildPaintProperties(*this) !=
+      CompositingReason::kNone) {
+    SVGLayoutSupport::NotifySVGRootOfChangedCompositingReasons(this);
+  }
+}
+
+void LayoutSVGModelObject::WillBeRemovedFromTree() {
+  LayoutObject::WillBeRemovedFromTree();
+  if (CompositingReasonFinder::DirectReasonsForSVGChildPaintProperties(*this) !=
+      CompositingReason::kNone) {
+    SVGLayoutSupport::NotifySVGRootOfChangedCompositingReasons(this);
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.h b/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.h
index 9118a3f..4bf6cd8 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.h
@@ -80,6 +80,9 @@
  protected:
   void WillBeDestroyed() override;
 
+  void InsertedIntoTree() override;
+  void WillBeRemovedFromTree() override;
+
   AffineTransform CalculateLocalTransform() const;
   bool CheckForImplicitTransformChange(bool bbox_changed) const;
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
index d01837fe..86c03d2 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
@@ -54,7 +54,9 @@
       needs_boundaries_or_transform_update_(true),
       has_box_decoration_background_(false),
       has_non_isolated_blending_descendants_(false),
-      has_non_isolated_blending_descendants_dirty_(false) {
+      has_non_isolated_blending_descendants_dirty_(false),
+      has_descendant_with_compositing_reason_(false),
+      has_descendant_with_compositing_reason_dirty_(false) {
   auto* svg = To<SVGSVGElement>(node);
   DCHECK(svg);
 
@@ -190,6 +192,15 @@
   UpdateLogicalWidth();
   UpdateLogicalHeight();
 
+  // Whether we have a self-painting layer depends on whether there are
+  // compositing descendants (see: |HasCompositingDescendants()| which is called
+  // from |PaintLayer::UpdateSelfPaintingLayer()|). We cannot do this update in
+  // StyleDidChange because descendants have not yet run StyleDidChange, so we
+  // don't know their compositing reasons yet. A layout is scheduled when
+  // |HasCompositingDescendants()| changes to ensure this is run.
+  if (Layer() && RuntimeEnabledFeatures::CompositeSVGEnabled())
+    Layer()->UpdateSelfPaintingLayer();
+
   // The local-to-border-box transform is a function with the following as
   // input:
   //
@@ -543,4 +554,50 @@
   return false;
 }
 
+void LayoutSVGRoot::NotifyDescendantCompositingReasonsChanged() {
+  if (has_descendant_with_compositing_reason_dirty_)
+    return;
+  has_descendant_with_compositing_reason_dirty_ = true;
+  SetNeedsLayout(layout_invalidation_reason::kSvgChanged);
+}
+
+PaintLayerType LayoutSVGRoot::LayerTypeRequired() const {
+  auto layer_type_required = LayoutReplaced::LayerTypeRequired();
+  if (layer_type_required == kNoPaintLayer &&
+      RuntimeEnabledFeatures::CompositeSVGEnabled() &&
+      !RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+    // Force a paint layer so a GraphicsLayer can be created if there are
+    // directly-composited descendants.
+    layer_type_required = kForcedPaintLayer;
+  }
+  return layer_type_required;
+}
+
+CompositingReasons LayoutSVGRoot::AdditionalCompositingReasons() const {
+  return RuntimeEnabledFeatures::CompositeSVGEnabled() &&
+                 HasDescendantWithCompositingReason()
+             ? CompositingReason::kSVGRoot
+             : CompositingReason::kNone;
+}
+
+bool LayoutSVGRoot::HasDescendantWithCompositingReason() const {
+  if (has_descendant_with_compositing_reason_dirty_) {
+    has_descendant_with_compositing_reason_ = false;
+    for (const LayoutObject* object = FirstChild(); object;
+         // Do not consider descendants of <foreignObject>.
+         object = object->IsSVGForeignObject()
+                      ? object->NextInPreOrderAfterChildren(this)
+                      : object->NextInPreOrder(this)) {
+      DCHECK(object->IsSVGChild());
+      if (CompositingReasonFinder::DirectReasonsForSVGChildPaintProperties(
+              *object) != CompositingReason::kNone) {
+        has_descendant_with_compositing_reason_ = true;
+        break;
+      }
+    }
+    has_descendant_with_compositing_reason_dirty_ = false;
+  }
+  return has_descendant_with_compositing_reason_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.h b/third_party/blink/renderer/core/layout/svg/layout_svg_root.h
index 959fd1c..14e07d5 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.h
@@ -89,6 +89,11 @@
 
   bool HasNonIsolatedBlendingDescendants() const final;
 
+  bool HasDescendantCompositingReasons() const {
+    return AdditionalCompositingReasons() != CompositingReason::kNone;
+  }
+  void NotifyDescendantCompositingReasonsChanged();
+
   const char* GetName() const override { return "LayoutSVGRoot"; }
 
  private:
@@ -165,6 +170,11 @@
   // would normally not change under zoom. See: https://crbug.com/222786.
   double LogicalSizeScaleFactorForPercentageLengths() const;
 
+  PaintLayerType LayerTypeRequired() const override;
+  bool CanHaveAdditionalCompositingReasons() const override { return true; }
+  CompositingReasons AdditionalCompositingReasons() const override;
+  bool HasDescendantWithCompositingReason() const;
+
   LayoutObjectChildList children_;
   LayoutSize container_size_;
   FloatRect object_bounding_box_;
@@ -178,6 +188,8 @@
   bool has_box_decoration_background_ : 1;
   mutable bool has_non_isolated_blending_descendants_ : 1;
   mutable bool has_non_isolated_blending_descendants_dirty_ : 1;
+  mutable bool has_descendant_with_compositing_reason_ : 1;
+  mutable bool has_descendant_with_compositing_reason_dirty_ : 1;
 };
 
 DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutSVGRoot, IsSVGRoot());
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc
index 40e7ae0..f8b166a 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
 
 namespace blink {
 
@@ -125,4 +126,87 @@
   EXPECT_EQ(2, count);
 }
 
+class CompositeSVGLayoutSVGRootTest : public PaintTestConfigurations,
+                                      public LayoutSVGRootTest,
+                                      private ScopedCompositeSVGForTest {
+ public:
+  CompositeSVGLayoutSVGRootTest() : ScopedCompositeSVGForTest(true) {}
+};
+
+INSTANTIATE_PAINT_TEST_SUITE_P(CompositeSVGLayoutSVGRootTest);
+
+// A PaintLayer is needed for the purposes of creating a GraphicsLayer to limit
+// CompositeSVG to SVG subtrees. This PaintLayer will not be needed with
+// CompositeAfterPaint. If compositing is needed for descendants, the paint
+// layer should be self-painting. Otherwise, it should be non-self-painting.
+TEST_P(CompositeSVGLayoutSVGRootTest, PaintLayerType) {
+  SetBodyInnerHTML(R"HTML(
+    <svg id="root" style="width: 200px; height: 200px;">
+      <rect id="rect" width="100" height="100" fill="green"/>
+    </svg>
+  )HTML");
+
+  const LayoutSVGRoot& root =
+      *ToLayoutSVGRoot(GetLayoutObjectByElementId("root"));
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+    EXPECT_FALSE(root.Layer());
+  else
+    EXPECT_FALSE(root.Layer()->IsSelfPaintingLayer());
+
+  GetDocument().getElementById("rect")->setAttribute("style",
+                                                     "will-change: transform");
+  UpdateAllLifecyclePhasesForTest();
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+    EXPECT_FALSE(root.Layer());
+  else
+    EXPECT_TRUE(root.Layer()->IsSelfPaintingLayer());
+
+  GetDocument().getElementById("rect")->removeAttribute("style");
+  UpdateAllLifecyclePhasesForTest();
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+    EXPECT_FALSE(root.Layer());
+  else
+    EXPECT_FALSE(root.Layer()->IsSelfPaintingLayer());
+}
+
+TEST_P(CompositeSVGLayoutSVGRootTest, HasDescendantCompositingReasons) {
+  SetBodyInnerHTML(R"HTML(
+    <svg id="root" style="width: 200px; height: 200px;">
+      <rect id="rect" width="100" height="100" fill="green"/>
+      <text id="text" x="10" y="30">
+        text
+        <tspan id="tspan">tspan</tspan>
+      </text>
+    </svg>
+  )HTML");
+
+  const LayoutSVGRoot& root =
+      *ToLayoutSVGRoot(GetLayoutObjectByElementId("root"));
+  EXPECT_FALSE(root.HasDescendantCompositingReasons());
+
+  GetDocument().getElementById("rect")->setAttribute("style",
+                                                     "will-change: transform");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_TRUE(root.HasDescendantCompositingReasons());
+  GetDocument().getElementById("rect")->removeAttribute("style");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(root.HasDescendantCompositingReasons());
+
+  GetDocument().getElementById("text")->setAttribute("style",
+                                                     "will-change: transform");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_TRUE(root.HasDescendantCompositingReasons());
+  GetDocument().getElementById("text")->removeAttribute("style");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(root.HasDescendantCompositingReasons());
+
+  GetDocument().getElementById("tspan")->setAttribute("style",
+                                                      "will-change: transform");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_TRUE(root.HasDescendantCompositingReasons());
+  GetDocument().getElementById("tspan")->removeAttribute("style");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(root.HasDescendantCompositingReasons());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc b/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc
index 996b0bb..15e9eb4e 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc
@@ -697,4 +697,15 @@
       .layout_object;
 }
 
+void SVGLayoutSupport::NotifySVGRootOfChangedCompositingReasons(
+    const LayoutObject* object) {
+  for (auto* ancestor = object->Parent(); ancestor;
+       ancestor = ancestor->Parent()) {
+    if (ancestor->IsSVGRoot()) {
+      ToLayoutSVGRoot(ancestor)->NotifyDescendantCompositingReasonsChanged();
+      break;
+    }
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/svg/svg_layout_support.h b/third_party/blink/renderer/core/layout/svg/svg_layout_support.h
index 214bf35..39987e8 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_layout_support.h
+++ b/third_party/blink/renderer/core/layout/svg/svg_layout_support.h
@@ -164,6 +164,8 @@
   static LayoutObject* FindClosestLayoutSVGText(const LayoutObject*,
                                                 const FloatPoint&);
 
+  static void NotifySVGRootOfChangedCompositingReasons(const LayoutObject*);
+
  private:
   static void UpdateObjectBoundingBox(FloatRect& object_bounding_box,
                                       bool& object_bounding_box_valid,
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 50c01cb6..f205418 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1740,10 +1740,9 @@
   WillCommitNavigation();
 
   DocumentInit init = DocumentInit::Create()
-                          .WithDocumentLoader(this)
+                          .WithDocumentLoader(this, owner_document)
                           .WithURL(Url())
                           .WithTypeFrom(MimeType())
-                          .WithOwnerDocument(owner_document)
                           .WithSrcdocDocument(loading_srcdoc_)
                           .WithSandboxFlags(sandbox_flags)
                           .WithNewRegistrationContext()
diff --git a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
index ff9ae0c..a2d2cbb 100644
--- a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
+++ b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
@@ -463,12 +463,6 @@
     bool is_script_origin_secure,
     const String& token) {
   DCHECK(!token.IsEmpty());
-
-  if (!trial_token_validator_) {
-    RecordTokenValidationResultHistogram(OriginTrialTokenStatus::kNotSupported);
-    return false;
-  }
-
   bool valid = false;
   StringUTF8Adaptor token_string(token);
   url::Origin script_url_origin;
diff --git a/third_party/blink/renderer/core/paint/clip_path_clipper.cc b/third_party/blink/renderer/core/paint/clip_path_clipper.cc
index fdd4d1a..2badeda6 100644
--- a/third_party/blink/renderer/core/paint/clip_path_clipper.cc
+++ b/third_party/blink/renderer/core/paint/clip_path_clipper.cc
@@ -177,7 +177,9 @@
                                                   DisplayItem::kSVGClip))
     return;
 
-  DrawingRecorder recorder(context, display_item_client, DisplayItem::kSVGClip);
+  DrawingRecorder recorder(
+      context, display_item_client, DisplayItem::kSVGClip,
+      EnclosingIntRect(properties->MaskClip()->UnsnappedClipRect().Rect()));
   context.Save();
   context.Translate(paint_offset.left, paint_offset.top);
 
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index edde229..9a773950 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -2115,6 +2115,10 @@
   return owning_layer_.IsUnderSVGHiddenContainer();
 }
 
+bool CompositedLayerMapping::IsSVGRoot() const {
+  return GetLayoutObject().IsSVGRoot();
+}
+
 bool CompositedLayerMapping::IsTrackingRasterInvalidations() const {
   return GetLayoutObject().GetFrameView()->IsTrackingRasterInvalidations();
 }
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
index 28cd8d2..5873234 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
@@ -180,6 +180,7 @@
                      const IntRect& interest_rect) const override;
   bool ShouldThrottleRendering() const override;
   bool IsUnderSVGHiddenContainer() const override;
+  bool IsSVGRoot() const override;
   bool IsTrackingRasterInvalidations() const override;
   void GraphicsLayersDidChange() override;
   bool PaintBlockedByDisplayLockIncludingAncestors(
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
index 6d9d6eb..229a9836 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
@@ -120,8 +120,11 @@
 CompositingReasons CompositingReasonFinder::DirectReasonsForPaintProperties(
     const LayoutObject& object) {
   // TODO(wangxianzhu): Don't depend on PaintLayer for CompositeAfterPaint.
-  if (!object.HasLayer())
+  if (!object.HasLayer()) {
+    if (object.IsSVGChild())
+      return DirectReasonsForSVGChildPaintProperties(object);
     return CompositingReason::kNone;
+  }
 
   const ComputedStyle& style = object.StyleRef();
   auto reasons = CompositingReasonsForAnimation(object) |
@@ -179,6 +182,18 @@
   return reasons;
 }
 
+CompositingReasons
+CompositingReasonFinder::DirectReasonsForSVGChildPaintProperties(
+    const LayoutObject& object) {
+  DCHECK(object.IsSVGChild());
+  if (RuntimeEnabledFeatures::CompositeSVGEnabled()) {
+    const ComputedStyle& style = object.StyleRef();
+    return CompositingReasonsForAnimation(object) |
+           CompositingReasonsForWillChange(style);
+  }
+  return CompositingReason::kNone;
+}
+
 CompositingReasons CompositingReasonFinder::CompositingReasonsFor3DTransform(
     const LayoutObject& layout_object) {
   // Note that we ask the layoutObject if it has a transform, because the
@@ -270,8 +285,10 @@
     return reasons;
 
   // Transforms don't apply on non-replaced inline elements.
-  // TODO(crbug.com/666244): Support composited transform animation for SVG.
-  if (object.IsBox() && style.HasCurrentTransformAnimation())
+  bool supports_composited_animations =
+      object.IsBox() ||
+      (RuntimeEnabledFeatures::CompositeSVGEnabled() && object.IsSVGChild());
+  if (supports_composited_animations && style.HasCurrentTransformAnimation())
     reasons |= CompositingReason::kActiveTransformAnimation;
   if (style.HasCurrentOpacityAnimation())
     reasons |= CompositingReason::kActiveOpacityAnimation;
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h
index 4ff02d7..c162205 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.h
@@ -33,6 +33,9 @@
   static CompositingReasons DirectReasonsForPaintProperties(
       const LayoutObject&);
 
+  static CompositingReasons DirectReasonsForSVGChildPaintProperties(
+      const LayoutObject&);
+
   static CompositingReasons CompositingReasonsForAnimation(const LayoutObject&);
   static CompositingReasons CompositingReasonsForWillChange(
       const ComputedStyle&);
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
index 4edb395..a877f9e 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
@@ -90,6 +90,10 @@
         DocumentUpdateReason::kTest);
   }
 
+  cc::PropertyTrees* GetPropertyTrees() {
+    return LayerTreeHost()->property_trees();
+  }
+
  private:
   PaintArtifactCompositor* paint_artifact_compositor() {
     return GetLocalFrameView()->GetPaintArtifactCompositor();
@@ -223,6 +227,70 @@
   EXPECT_TRUE(layer->has_will_change_transform_hint());
 }
 
+TEST_P(CompositingTest, WillChangeTransformHintInSVG) {
+  ScopedCompositeSVGForTest enable_feature(true);
+  InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
+    <!doctype html>
+    <style>
+      #willChange {
+        width: 100px;
+        height: 100px;
+        will-change: transform;
+      }
+    </style>
+    <svg width="200" height="200">
+      <rect id="willChange" fill="blue"></rect>
+    </svg>
+  )HTML");
+  UpdateAllLifecyclePhases();
+  auto* layer = CcLayerByDOMElementId("willChange");
+  EXPECT_TRUE(layer->has_will_change_transform_hint());
+}
+
+TEST_P(CompositingTest, PaintPropertiesWhenCompositingSVG) {
+  ScopedCompositeSVGForTest enable_feature(true);
+  InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
+    <!doctype html>
+    <style>
+      #ancestor {
+        opacity: 0.9;
+      }
+      #svg {
+        opacity: 0.8;
+      }
+      #rect {
+        width: 100px;
+        height: 100px;
+        will-change: transform;
+        opacity: 0.7;
+      }
+    </style>
+    <div id="ancestor">
+      <svg id="svg" width="200" height="200">
+        <rect width="10" height="10" fill="red"></rect>
+        <rect id="rect" fill="blue" stroke-width="1" stroke="black"></rect>
+      </svg>
+    </div>
+  )HTML");
+  UpdateAllLifecyclePhases();
+  auto* ancestor = CcLayerByDOMElementId("ancestor");
+  auto* ancestor_effect_node =
+      GetPropertyTrees()->effect_tree.Node(ancestor->effect_tree_index());
+  EXPECT_EQ(ancestor_effect_node->opacity, 0.9f);
+
+  auto* svg_root = CcLayerByDOMElementId("svg");
+  auto* svg_root_effect_node =
+      GetPropertyTrees()->effect_tree.Node(svg_root->effect_tree_index());
+  EXPECT_EQ(svg_root_effect_node->opacity, 0.8f);
+  EXPECT_EQ(svg_root_effect_node->parent_id, ancestor_effect_node->id);
+
+  auto* rect = CcLayerByDOMElementId("rect");
+  auto* rect_effect_node =
+      GetPropertyTrees()->effect_tree.Node(rect->effect_tree_index());
+  EXPECT_EQ(rect_effect_node->opacity, 0.7f);
+  EXPECT_EQ(rect_effect_node->parent_id, svg_root_effect_node->id);
+}
+
 TEST_P(CompositingTest, BackgroundColorInScrollingContentsLayer) {
   InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(), R"HTML(
     <style>
@@ -640,6 +708,47 @@
   EXPECT_FALSE(transform_node->transform_changed);
 }
 
+TEST_P(CompositingSimTest, DirectSVGTransformPropertyUpdate) {
+  ScopedCompositeSVGForTest enable_feature(true);
+  InitializeWithHTML(R"HTML(
+    <!doctype html>
+    <style>
+      #willChange {
+        width: 100px;
+        height: 100px;
+        will-change: transform;
+        transform: translate(10px, 10px);
+      }
+    </style>
+    <svg width="200" height="200">
+      <rect id="willChange" fill="blue"></rect>
+    </svg>
+  )HTML");
+
+  Compositor().BeginFrame();
+
+  auto* will_change_layer = CcLayerByDOMElementId("willChange");
+  auto transform_tree_index = will_change_layer->transform_tree_index();
+  auto* transform_node =
+      GetPropertyTrees()->transform_tree.Node(transform_tree_index);
+
+  // Initially, transform should be unchanged.
+  EXPECT_FALSE(transform_node->transform_changed);
+  EXPECT_FALSE(paint_artifact_compositor()->NeedsUpdate());
+
+  // Modifying the transform in a simple way allowed for a direct update.
+  auto* will_change_element = GetElementById("willChange");
+  will_change_element->setAttribute(html_names::kStyleAttr,
+                                    "transform: translate(30px, 20px)");
+  UpdateAllLifecyclePhasesExceptPaint();
+  EXPECT_TRUE(transform_node->transform_changed);
+  EXPECT_FALSE(paint_artifact_compositor()->NeedsUpdate());
+
+  // After a frame the |transform_changed| value should be reset.
+  Compositor().BeginFrame();
+  EXPECT_FALSE(transform_node->transform_changed);
+}
+
 // This test is similar to |DirectTransformPropertyUpdate| but tests that
 // the changed value of a directly updated transform is still set if some other
 // change causes PaintArtifactCompositor to run and do non-direct updates.
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 6e30524e..8a7e6dec 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -2972,7 +2972,10 @@
 bool PaintLayer::ShouldBeSelfPaintingLayer() const {
   return GetLayoutObject().LayerTypeRequired() == kNormalPaintLayer ||
          (scrollable_area_ && scrollable_area_->HasOverlayOverflowControls()) ||
-         ScrollsOverflow();
+         ScrollsOverflow() ||
+         (RuntimeEnabledFeatures::CompositeSVGEnabled() &&
+          GetLayoutObject().IsSVGRoot() &&
+          ToLayoutSVGRoot(GetLayoutObject()).HasDescendantCompositingReasons());
 }
 
 void PaintLayer::UpdateSelfPaintingLayer() {
@@ -3403,11 +3406,19 @@
 void PaintLayer::SetNeedsRepaint() {
   SetSelfNeedsRepaint();
 
-  // If you need repaint, then you might issue raster invalidations, and in
-  // Composite after Paint mode, we do these in PAC::Update().
   LocalFrameView* frame_view = GetLayoutObject().GetDocument().View();
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && frame_view) {
-    frame_view->SetPaintArtifactCompositorNeedsUpdate();
+  if (frame_view) {
+    // If you need repaint, then you might issue raster invalidations, and in
+    // Composite after Paint mode, we do these in PAC::Update().
+    if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+      frame_view->SetPaintArtifactCompositorNeedsUpdate();
+    } else if (RuntimeEnabledFeatures::CompositeSVGEnabled()) {
+      // With GraphicsLayer layerization after paint, we also need to call
+      // PaintArtifactCompositor::Update for raster invalidations.
+      // TODO(pdr): Can we do a lighter-weight update that only does raster
+      // invalidation and skips the overlap test?
+      frame_view->SetPaintArtifactCompositorNeedsUpdate();
+    }
   }
 
   // Do this unconditionally to ensure container chain is marked when
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 4679744..49c2717 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -195,7 +195,7 @@
   ALWAYS_INLINE void UpdateClipPathCache();
   ALWAYS_INLINE void UpdateStickyTranslation();
   ALWAYS_INLINE void UpdateTransform();
-  ALWAYS_INLINE void UpdateTransformForSVGChild();
+  ALWAYS_INLINE void UpdateTransformForSVGChild(CompositingReasons);
   ALWAYS_INLINE bool EffectCanUseCurrentClipAsOutputClip() const;
   ALWAYS_INLINE void UpdateEffect();
   ALWAYS_INLINE void UpdateFilter();
@@ -612,21 +612,27 @@
     context_.current.transform = properties_->StickyTranslation();
 }
 
-static bool NeedsTransformForSVGChild(const LayoutObject& object) {
+static bool NeedsTransformForSVGChild(
+    const LayoutObject& object,
+    CompositingReasons direct_compositing_reasons) {
+  if (!object.IsSVGChild() || object.IsText())
+    return false;
+  if (direct_compositing_reasons &
+      CompositingReasonFinder::DirectReasonsForSVGChildPaintProperties(object))
+    return true;
   // TODO(pdr): Check for the presence of a transform instead of the value.
   // Checking for an identity matrix will cause the property tree structure
   // to change during animations if the animation passes through the
   // identity matrix.
-  // TODO(crbug.com/666244): Check CompositingReasonsForAnimation here when we
-  // support composited transform animations in SVG.
-  return object.IsSVGChild() && !object.IsText() &&
-         !object.LocalToSVGParentTransform().IsIdentity();
+  return !object.LocalToSVGParentTransform().IsIdentity();
 }
 
 static void SetTransformNodeStateFromAffineTransform(
     TransformPaintPropertyNode::State& state,
-    const AffineTransform& transform) {
-  if (transform.IsIdentityOrTranslation())
+    const AffineTransform& transform,
+    bool disable_2d_translation_optimization) {
+  if (!disable_2d_translation_optimization &&
+      transform.IsIdentityOrTranslation())
     state.transform_and_origin = {FloatSize(transform.E(), transform.F())};
   else
     state.transform_and_origin = {TransformationMatrix(transform)};
@@ -634,7 +640,8 @@
 
 // SVG does not use the general transform update of |UpdateTransform|, instead
 // creating a transform node for SVG-specific transforms without 3D.
-void FragmentPaintPropertyTreeBuilder::UpdateTransformForSVGChild() {
+void FragmentPaintPropertyTreeBuilder::UpdateTransformForSVGChild(
+    CompositingReasons direct_compositing_reasons) {
   DCHECK(properties_);
   DCHECK(object_.IsSVGChild());
   // SVG does not use paint offset internally, except for SVGForeignObject which
@@ -644,14 +651,49 @@
 
   if (NeedsPaintPropertyUpdate()) {
     AffineTransform transform = object_.LocalToSVGParentTransform();
-    if (NeedsTransformForSVGChild(object_)) {
+    if (NeedsTransformForSVGChild(object_, direct_compositing_reasons)) {
       // The origin is included in the local transform, so leave origin empty.
-      // TODO(crbug.com/666244): Support composited transform animation for SVG
-      // using similar code as |UpdateTransform| for animations.
       TransformPaintPropertyNode::State state;
-      SetTransformNodeStateFromAffineTransform(state, transform);
-      OnUpdate(properties_->UpdateTransform(*context_.current.transform,
-                                            std::move(state)));
+      bool disable_2d_translation_optimization =
+          full_context_.direct_compositing_reasons &
+          CompositingReason::kActiveTransformAnimation;
+      SetTransformNodeStateFromAffineTransform(
+          state, transform, disable_2d_translation_optimization);
+
+      // TODO(pdr): There is additional logic in
+      // FragmentPaintPropertyTreeBuilder::UpdateTransform that likely needs to
+      // be included here, such as setting rendering context ids, etc.
+      if (RuntimeEnabledFeatures::CompositeSVGEnabled()) {
+        state.direct_compositing_reasons =
+            direct_compositing_reasons &
+            CompositingReasonFinder::DirectReasonsForSVGChildPaintProperties(
+                object_);
+        state.flags.flattens_inherited_transform =
+            context_.current.should_flatten_inherited_transform;
+        state.compositor_element_id = GetCompositorElementId(
+            CompositorElementIdNamespace::kPrimaryTransform);
+      }
+
+      TransformPaintPropertyNode::AnimationState animation_state;
+      animation_state.is_running_animation_on_compositor =
+          object_.StyleRef().IsRunningTransformAnimationOnCompositor();
+      auto effective_change_type = properties_->UpdateTransform(
+          *context_.current.transform, std::move(state), animation_state);
+      if (effective_change_type ==
+              PaintPropertyChangeType::kChangedOnlySimpleValues &&
+          properties_->Transform()->HasDirectCompositingReasons()) {
+        if (auto* paint_artifact_compositor =
+                object_.GetFrameView()->GetPaintArtifactCompositor()) {
+          bool updated = paint_artifact_compositor->DirectlyUpdateTransform(
+              *properties_->Transform());
+          if (updated) {
+            effective_change_type =
+                PaintPropertyChangeType::kChangedOnlyCompositedValues;
+            properties_->Transform()->CompositorSimpleValuesUpdated();
+          }
+        }
+      }
+      OnUpdate(effective_change_type);
     } else {
       OnClear(properties_->ClearTransform());
     }
@@ -734,7 +776,7 @@
 
 void FragmentPaintPropertyTreeBuilder::UpdateTransform() {
   if (object_.IsSVGChild()) {
-    UpdateTransformForSVGChild();
+    UpdateTransformForSVGChild(full_context_.direct_compositing_reasons);
     return;
   }
 
@@ -1222,13 +1264,15 @@
       EffectPaintPropertyNode::State state;
       state.local_transform_space = context_.current.transform;
 
-      if (auto* layer = ToLayoutBoxModelObject(object_).Layer()) {
-        // Try to use the cached filter.
-        if (properties_->Filter())
-          state.filter = properties_->Filter()->Filter();
-        layer->UpdateFilterReferenceBox();
-        layer->UpdateCompositorFilterOperationsForFilter(state.filter);
-        layer->ClearFilterOnEffectNodeDirty();
+      if (object_.IsBoxModelObject()) {
+        if (auto* layer = ToLayoutBoxModelObject(object_).Layer()) {
+          // Try to use the cached filter.
+          if (properties_->Filter())
+            state.filter = properties_->Filter()->Filter();
+          layer->UpdateFilterReferenceBox();
+          layer->UpdateCompositorFilterOperationsForFilter(state.filter);
+          layer->ClearFilterOnEffectNodeDirty();
+        }
       }
 
       // The CSS filter spec didn't specify how filters interact with overflow
@@ -1763,7 +1807,8 @@
     }
     if (!content_to_parent_space.IsIdentity()) {
       TransformPaintPropertyNode::State state;
-      SetTransformNodeStateFromAffineTransform(state, content_to_parent_space);
+      SetTransformNodeStateFromAffineTransform(state, content_to_parent_space,
+                                               false);
       state.flags.flattens_inherited_transform =
           context_.current.should_flatten_inherited_transform;
       OnUpdate(properties_->UpdateReplacedContentTransform(
@@ -3411,7 +3456,8 @@
        // the paint offset and border box has been computed.
        MayNeedClipPathClip(object_) ||
        NeedsEffect(object_, context_.direct_compositing_reasons) ||
-       NeedsTransformForSVGChild(object_) ||
+       NeedsTransformForSVGChild(object_,
+                                 context_.direct_compositing_reasons) ||
        NeedsFilter(object_, context_.direct_compositing_reasons) ||
        NeedsCssClip(object_) || NeedsInnerBorderRadiusClip(object_) ||
        NeedsOverflowClip(object_) || NeedsPerspective(object_) ||
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index 62affd8..6d4877d4 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -5613,12 +5613,6 @@
       DocScrollTranslation(&ChildDocument())->HasDirectCompositingReasons());
 }
 
-TEST_P(PaintPropertyTreeBuilderTest,
-       NoTransformPropertyForWillChangeWithoutLayer) {
-  SetBodyInnerHTML("<svg id='target' style='will-change: left'></svg>");
-  EXPECT_EQ(nullptr, PaintPropertiesForElement("target")->Transform());
-}
-
 TEST_P(PaintPropertyTreeBuilderTest, OmitOverflowClip) {
   SetBodyInnerHTML(R"HTML(
     <style>
diff --git a/third_party/blink/renderer/core/paint/scoped_svg_paint_state.cc b/third_party/blink/renderer/core/paint/scoped_svg_paint_state.cc
index 4c78d7e0..9370a57 100644
--- a/third_party/blink/renderer/core/paint/scoped_svg_paint_state.cc
+++ b/third_party/blink/renderer/core/paint/scoped_svg_paint_state.cc
@@ -72,14 +72,15 @@
                                                   DisplayItem::kSVGFilter))
     return;
 
-  DrawingRecorder recorder(context, display_item_client,
-                           DisplayItem::kSVGFilter);
-  sk_sp<PaintFilter> image_filter = filter_data->CreateFilter();
-  context.Save();
-
   // Clip drawing of filtered image to the minimum required paint rect.
   const FloatRect object_bounds = object.StrokeBoundingBox();
   const FloatRect paint_rect = filter_data->MapRect(object_bounds);
+
+  DrawingRecorder recorder(context, display_item_client,
+                           DisplayItem::kSVGFilter,
+                           EnclosingIntRect(paint_rect));
+  sk_sp<PaintFilter> image_filter = filter_data->CreateFilter();
+  context.Save();
   context.ClipRect(paint_rect);
 
   // Use the union of the pre-image and the post-image as the layer bounds.
diff --git a/third_party/blink/renderer/core/paint/svg_image_painter.cc b/third_party/blink/renderer/core/paint/svg_image_painter.cc
index 4d63050..74472ee 100644
--- a/third_party/blink/renderer/core/paint/svg_image_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_image_painter.cc
@@ -46,9 +46,9 @@
             paint_state.GetPaintInfo().phase)) {
       SVGModelObjectPainter::RecordHitTestData(layout_svg_image_,
                                                paint_state.GetPaintInfo());
-      DrawingRecorder recorder(paint_state.GetPaintInfo().context,
-                               layout_svg_image_,
-                               paint_state.GetPaintInfo().phase);
+      SVGDrawingRecorder recorder(paint_state.GetPaintInfo().context,
+                                  layout_svg_image_,
+                                  paint_state.GetPaintInfo().phase);
       PaintForeground(paint_state.GetPaintInfo());
     }
   }
diff --git a/third_party/blink/renderer/core/paint/svg_mask_painter.cc b/third_party/blink/renderer/core/paint/svg_mask_painter.cc
index b579318..2c486ab 100644
--- a/third_party/blink/renderer/core/paint/svg_mask_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_mask_painter.cc
@@ -30,15 +30,24 @@
   // properties are ready.
   if (!properties || !properties->Mask())
     return;
+
+  DCHECK(properties->MaskClip());
+  PropertyTreeStateOrAlias property_tree_state(
+      properties->Mask()->LocalTransformSpace(), *properties->MaskClip(),
+      *properties->Mask());
   ScopedPaintChunkProperties scoped_paint_chunk_properties(
-      context_.GetPaintController(), *properties->Mask(), display_item_client_,
+      context_.GetPaintController(), property_tree_state, display_item_client_,
       DisplayItem::kSVGMask);
 
   if (DrawingRecorder::UseCachedDrawingIfPossible(
           context_, display_item_client_, DisplayItem::kSVGMask))
     return;
+
+  FloatRect visual_rect = properties->MaskClip()->UnsnappedClipRect().Rect();
+  visual_rect.Intersect(layout_object_.VisualRectInLocalSVGCoordinates());
   DrawingRecorder recorder(context_, display_item_client_,
-                           DisplayItem::kSVGMask);
+                           DisplayItem::kSVGMask,
+                           EnclosingIntRect(visual_rect));
 
   const SVGComputedStyle& svg_style = layout_object_.StyleRef().SvgStyle();
   auto* masker =
diff --git a/third_party/blink/renderer/core/paint/svg_model_object_painter.h b/third_party/blink/renderer/core/paint/svg_model_object_painter.h
index 21578ed..8d4367f8 100644
--- a/third_party/blink/renderer/core/paint/svg_model_object_painter.h
+++ b/third_party/blink/renderer/core/paint/svg_model_object_painter.h
@@ -5,13 +5,12 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_MODEL_OBJECT_PAINTER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_MODEL_OBJECT_PAINTER_H_
 
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/core/layout/svg/layout_svg_model_object.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 
 namespace blink {
 
 struct PaintInfo;
-class LayoutObject;
-class LayoutSVGModelObject;
 
 class SVGModelObjectPainter {
   STACK_ALLOCATED();
@@ -39,6 +38,39 @@
   const LayoutSVGModelObject& layout_svg_model_object_;
 };
 
+// A wrapper of DrawingRecorder for SVG children, providing the default visual
+// rect (see DisplayItem::VisualRect() for definition) for the SVG contents
+// not including outlines. Using a template so that
+// VisualRectInLocalSVGCoordinates() can be called directly instead of through
+// vtable.
+class SVGDrawingRecorder : public DrawingRecorder {
+ public:
+  template <typename LayoutObjectType>
+  SVGDrawingRecorder(GraphicsContext& context,
+                     const LayoutObjectType& object,
+                     DisplayItem::Type type)
+      : DrawingRecorder(
+            context,
+            object,
+            type,
+            EnclosingIntRect(object.VisualRectInLocalSVGCoordinates())) {
+    DCHECK(object.IsSVGChild());
+    // We should not use this for SVG containers which paint effects only,
+    // while VisualRectInLocalSVGCoordinates() contains visual rects from
+    // children which are not painted by the container. We calculate the correct
+    // visual rect when painting effects.
+    DCHECK(!object.IsSVGContainer());
+  }
+
+  template <typename LayoutObjectType>
+  SVGDrawingRecorder(GraphicsContext& context,
+                     const LayoutObjectType& object,
+                     PaintPhase phase)
+      : SVGDrawingRecorder(context,
+                           object,
+                           DisplayItem::PaintPhaseToDrawingType(phase)) {}
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_MODEL_OBJECT_PAINTER_H_
diff --git a/third_party/blink/renderer/core/paint/svg_root_inline_box_painter.cc b/third_party/blink/renderer/core/paint/svg_root_inline_box_painter.cc
index ba48eea..a792dd8 100644
--- a/third_party/blink/renderer/core/paint/svg_root_inline_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_root_inline_box_painter.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/paint/scoped_svg_paint_state.h"
 #include "third_party/blink/renderer/core/paint/svg_inline_flow_box_painter.h"
 #include "third_party/blink/renderer/core/paint/svg_inline_text_box_painter.h"
+#include "third_party/blink/renderer/core/paint/svg_model_object_painter.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 
 namespace blink {
@@ -24,15 +25,13 @@
   bool has_selection =
       !paint_info.IsPrinting() && svg_root_inline_box_.IsSelected();
 
-  if (has_selection && !DrawingRecorder::UseCachedDrawingIfPossible(
-                           paint_info.context,
-                           *LineLayoutAPIShim::ConstLayoutObjectFrom(
-                               svg_root_inline_box_.GetLineLayoutItem()),
-                           paint_info.phase)) {
-    DrawingRecorder recorder(paint_info.context,
-                             *LineLayoutAPIShim::ConstLayoutObjectFrom(
-                                 svg_root_inline_box_.GetLineLayoutItem()),
-                             paint_info.phase);
+  const auto& layout_object = *LineLayoutAPIShim::ConstLayoutObjectFrom(
+      svg_root_inline_box_.GetLineLayoutItem());
+  if (has_selection &&
+      !DrawingRecorder::UseCachedDrawingIfPossible(
+          paint_info.context, layout_object, paint_info.phase)) {
+    SVGDrawingRecorder recorder(paint_info.context, layout_object,
+                                paint_info.phase);
     for (InlineBox* child = svg_root_inline_box_.FirstChild(); child;
          child = child->NextOnLine()) {
       if (auto* svg_inline_text_box = DynamicTo<SVGInlineTextBox>(child)) {
diff --git a/third_party/blink/renderer/core/paint/svg_shape_painter.cc b/third_party/blink/renderer/core/paint/svg_shape_painter.cc
index 57e599c0..cd517c0 100644
--- a/third_party/blink/renderer/core/paint/svg_shape_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_shape_painter.cc
@@ -63,9 +63,9 @@
             paint_state.GetPaintInfo().phase)) {
       SVGModelObjectPainter::RecordHitTestData(layout_svg_shape_,
                                                paint_state.GetPaintInfo());
-      DrawingRecorder recorder(paint_state.GetPaintInfo().context,
-                               layout_svg_shape_,
-                               paint_state.GetPaintInfo().phase);
+      SVGDrawingRecorder recorder(paint_state.GetPaintInfo().context,
+                                  layout_svg_shape_,
+                                  paint_state.GetPaintInfo().phase);
       const SVGComputedStyle& svg_style =
           layout_svg_shape_.StyleRef().SvgStyle();
 
diff --git a/third_party/blink/renderer/core/xml/dom_parser.cc b/third_party/blink/renderer/core/xml/dom_parser.cc
index c7e72fd..41a8d011 100644
--- a/third_party/blink/renderer/core/xml/dom_parser.cc
+++ b/third_party/blink/renderer/core/xml/dom_parser.cc
@@ -29,10 +29,9 @@
 
 Document* DOMParser::parseFromString(const String& str, const String& type) {
   Document* doc = DocumentInit::Create()
-                      .WithURL(GetDocument()->Url())
+                      .WithURL(window_->Url())
                       .WithTypeFrom(type)
                       .WithExecutionContext(window_)
-                      .WithOwnerDocument(GetDocument())
                       .CreateDocument();
   doc->SetContent(str);
   doc->SetMimeType(AtomicString(type));
@@ -47,8 +46,4 @@
   ScriptWrappable::Trace(visitor);
 }
 
-Document* DOMParser::GetDocument() const {
-  return window_->document();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/xml/dom_parser.h b/third_party/blink/renderer/core/xml/dom_parser.h
index b4b45c8..a8b067a 100644
--- a/third_party/blink/renderer/core/xml/dom_parser.h
+++ b/third_party/blink/renderer/core/xml/dom_parser.h
@@ -44,7 +44,7 @@
 
   void Trace(Visitor*) const override;
 
-  Document* GetDocument() const;
+  LocalDOMWindow* GetWindow() const { return window_.Get(); }
 
  private:
   WeakMember<LocalDOMWindow> window_;
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index 260fc6f..8e56eeb 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -353,7 +353,6 @@
   auto* document = To<LocalDOMWindow>(GetExecutionContext())->document();
   DocumentInit init = DocumentInit::Create()
                           .WithExecutionContext(GetExecutionContext())
-                          .WithOwnerDocument(document)
                           .WithURL(response_.ResponseUrl());
   if (is_html)
     response_document_ = MakeGarbageCollected<HTMLDocument>(init);
diff --git a/third_party/blink/renderer/modules/image_downloader/multi_resolution_image_resource_fetcher.cc b/third_party/blink/renderer/modules/image_downloader/multi_resolution_image_resource_fetcher.cc
index 0c0ee35..7f742db7d 100644
--- a/third_party/blink/renderer/modules/image_downloader/multi_resolution_image_resource_fetcher.cc
+++ b/third_party/blink/renderer/modules/image_downloader/multi_resolution_image_resource_fetcher.cc
@@ -122,7 +122,7 @@
   if (request_context == mojom::blink::RequestContextType::FAVICON) {
     // To prevent cache tainting, the cross-origin favicon requests have to
     // by-pass the service workers. This should ideally not happen. But Chrome’s
-    // ThumbnailDatabase is using the icon URL as a key of the "favicons" table.
+    // FaviconDatabase is using the icon URL as a key of the "favicons" table.
     // So if we don't set the skip flag here, malicious service workers can
     // override the favicon image of any origins.
     if (!frame->DomWindow()->GetSecurityOrigin()->CanAccess(
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_renderer_factory_impl.cc b/third_party/blink/renderer/modules/mediastream/media_stream_renderer_factory_impl.cc
index b9ee275..b90c6ad 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_renderer_factory_impl.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_renderer_factory_impl.cc
@@ -47,11 +47,6 @@
 
 }  // namespace
 
-std::unique_ptr<WebMediaStreamRendererFactory>
-CreateWebMediaStreamRendererFactory() {
-  return std::make_unique<MediaStreamRendererFactoryImpl>();
-}
-
 MediaStreamRendererFactoryImpl::MediaStreamRendererFactoryImpl() {}
 
 MediaStreamRendererFactoryImpl::~MediaStreamRendererFactoryImpl() {}
diff --git a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
index f5895c6b..87dedff6 100644
--- a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
+++ b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
@@ -33,9 +33,9 @@
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/public/platform/web_surface_layer_bridge.h"
 #include "third_party/blink/public/web/modules/media/webmediaplayer_util.h"
-#include "third_party/blink/public/web/modules/mediastream/web_media_stream_renderer_factory.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_local_frame_wrapper.h"
+#include "third_party/blink/renderer/modules/mediastream/media_stream_renderer_factory_impl.h"
 #include "third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
@@ -299,7 +299,6 @@
     WebMediaPlayerClient* client,
     WebMediaPlayerDelegate* delegate,
     std::unique_ptr<media::MediaLog> media_log,
-    std::unique_ptr<WebMediaStreamRendererFactory> factory,
     scoped_refptr<base::SingleThreadTaskRunner> main_render_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
@@ -320,7 +319,7 @@
       paused_(true),
       video_transformation_(media::kNoTransformation),
       media_log_(std::move(media_log)),
-      renderer_factory_(std::move(factory)),
+      renderer_factory_(std::make_unique<MediaStreamRendererFactoryImpl>()),
       main_render_task_runner_(std::move(main_render_task_runner)),
       io_task_runner_(std::move(io_task_runner)),
       compositor_task_runner_(std::move(compositor_task_runner)),
@@ -1319,6 +1318,11 @@
   frame_deliverer_->gpu_memory_buffer_pool_.reset(gpu_memory_buffer_pool);
 }
 
+void WebMediaPlayerMS::SetMediaStreamRendererFactoryForTesting(
+    std::unique_ptr<WebMediaStreamRendererFactory> renderer_factory) {
+  renderer_factory_ = std::move(renderer_factory);
+}
+
 void WebMediaPlayerMS::OnDisplayTypeChanged(
     WebMediaPlayer::DisplayType display_type) {
   if (!bridge_)
diff --git a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc
index 83de9f8..348e8dc1 100644
--- a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc
@@ -680,7 +680,6 @@
           : WebMediaPlayer::SurfaceLayerMode::kNever;
   player_ = std::make_unique<WebMediaPlayerMS>(
       nullptr, this, &delegate_, std::make_unique<media::NullMediaLog>(),
-      std::unique_ptr<WebMediaStreamRendererFactory>(render_factory_),
       scheduler::GetSingleThreadTaskRunnerForTesting(),
       scheduler::GetSingleThreadTaskRunnerForTesting(),
       scheduler::GetSingleThreadTaskRunnerForTesting(),
@@ -690,6 +689,8 @@
       WTF::Bind(&WebMediaPlayerMSTest::CreateMockSurfaceLayerBridge,
                 WTF::Unretained(this)),
       std::move(submitter_), surface_layer_mode);
+  player_->SetMediaStreamRendererFactoryForTesting(
+      std::unique_ptr<WebMediaStreamRendererFactory>(render_factory_));
 }
 
 MockMediaStreamVideoRenderer* WebMediaPlayerMSTest::LoadAndGetFrameProvider(
diff --git a/third_party/blink/renderer/modules/screen_enumeration/global_screen_enumeration.cc b/third_party/blink/renderer/modules/screen_enumeration/global_screen_enumeration.cc
index 56cd607..a3b8e147 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/global_screen_enumeration.cc
+++ b/third_party/blink/renderer/modules/screen_enumeration/global_screen_enumeration.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/public/mojom/screen_enumeration/screen_enumeration.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/screen.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -22,32 +23,54 @@
 
 namespace {
 
-void DidGetDisplays(
-    ScriptPromiseResolver* resolver,
-    mojo::Remote<mojom::blink::ScreenEnumeration>,
-    WTF::Vector<display::mojom::blink::DisplayPtr> backend_displays,
-    int64_t internal_id,
-    int64_t primary_id,
-    bool success) {
+void DidGetDisplays(ScriptPromiseResolver* resolver,
+                    mojo::Remote<mojom::blink::ScreenEnumeration>,
+                    mojom::blink::DisplaysPtr result) {
   ScriptState* script_state = resolver->GetScriptState();
   if (!script_state->ContextIsValid())
     return;
 
+  ScriptState::Scope scope(script_state);
+  if (result.is_null()) {
+    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+        script_state->GetIsolate(), DOMExceptionCode::kNotAllowedError,
+        "Screen information is unavailable or access is not allowed."));
+    return;
+  }
+
   HeapVector<Member<Screen>> screens;
-  screens.ReserveInitialCapacity(backend_displays.size());
-  for (display::mojom::blink::DisplayPtr& backend_display : backend_displays) {
-    const bool internal = backend_display->id == internal_id;
-    const bool primary = backend_display->id == primary_id;
+  screens.ReserveInitialCapacity(result->displays.size());
+  for (display::mojom::blink::DisplayPtr& display : result->displays) {
+    const bool internal = display->id == result->internal_id;
+    const bool primary = display->id == result->primary_id;
     // TODO(http://crbug.com/994889): Implement temporary, generated per-origin
     // unique device IDs that reset when cookies are deleted. See related:
     // web_bluetooth_device_id.h, unguessable_token.h, and uuid.h
     const String id = String::NumberToStringECMAScript(screens.size());
-    screens.emplace_back(MakeGarbageCollected<Screen>(
-        std::move(backend_display), internal, primary, id));
+    screens.emplace_back(MakeGarbageCollected<Screen>(std::move(display),
+                                                      internal, primary, id));
   }
   resolver->Resolve(std::move(screens));
 }
 
+void DidHasMultipleDisplays(ScriptPromiseResolver* resolver,
+                            mojo::Remote<mojom::blink::ScreenEnumeration>,
+                            mojom::blink::MultipleDisplays result) {
+  ScriptState* script_state = resolver->GetScriptState();
+  if (!script_state->ContextIsValid())
+    return;
+
+  ScriptState::Scope scope(script_state);
+  if (result == mojom::blink::MultipleDisplays::kError) {
+    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+        script_state->GetIsolate(), DOMExceptionCode::kNotAllowedError,
+        "Screen information is unavailable or access is not allowed."));
+    return;
+  }
+
+  resolver->Resolve(result == mojom::blink::MultipleDisplays::kTrue);
+}
+
 }  // namespace
 
 // static
@@ -55,18 +78,18 @@
     ScriptState* script_state,
     LocalDOMWindow&,
     ExceptionState& exception_state) {
+  if (!script_state->ContextIsValid()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "The execution context is not valid.");
+    return ScriptPromise();
+  }
+
   // TODO(msw): Cache the backend connection.
   mojo::Remote<mojom::blink::ScreenEnumeration> backend;
   ExecutionContext::From(script_state)
       ->GetBrowserInterfaceBroker()
       .GetInterface(backend.BindNewPipeAndPassReceiver());
 
-  if (!backend) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "ScreenEnumeration backend unavailable");
-    return ScriptPromise();
-  }
-
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   auto* raw_backend = backend.get();
   raw_backend->GetDisplays(
@@ -74,4 +97,28 @@
   return resolver->Promise();
 }
 
+// static
+ScriptPromise GlobalScreenEnumeration::isMultiScreen(
+    ScriptState* script_state,
+    LocalDOMWindow&,
+    ExceptionState& exception_state) {
+  if (!script_state->ContextIsValid()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "The execution context is not valid.");
+    return ScriptPromise();
+  }
+
+  // TODO(msw): Cache the backend connection.
+  mojo::Remote<mojom::blink::ScreenEnumeration> backend;
+  ExecutionContext::From(script_state)
+      ->GetBrowserInterfaceBroker()
+      .GetInterface(backend.BindNewPipeAndPassReceiver());
+
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  auto* raw_backend = backend.get();
+  raw_backend->HasMultipleDisplays(WTF::Bind(
+      &DidHasMultipleDisplays, WrapPersistent(resolver), std::move(backend)));
+  return resolver->Promise();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/screen_enumeration/global_screen_enumeration.h b/third_party/blink/renderer/modules/screen_enumeration/global_screen_enumeration.h
index de4ebc75..e00e525 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/global_screen_enumeration.h
+++ b/third_party/blink/renderer/modules/screen_enumeration/global_screen_enumeration.h
@@ -22,9 +22,14 @@
 
  public:
   // Resolves to the list of |Screen| objects in the device's screen space.
-  static ScriptPromise getScreens(ScriptState*,
+  static ScriptPromise getScreens(ScriptState* script_state,
                                   LocalDOMWindow&,
-                                  ExceptionState&);
+                                  ExceptionState& exception_state);
+
+  // Resolves to true if the number of available screens is greater than one.
+  static ScriptPromise isMultiScreen(ScriptState* script_state,
+                                     LocalDOMWindow&,
+                                     ExceptionState& exception_state);
 
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(screenschange, kScreenschange)
 };
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screen_enumeration.idl b/third_party/blink/renderer/modules/screen_enumeration/screen_enumeration.idl
index cf0810d6..9430e6d 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screen_enumeration.idl
+++ b/third_party/blink/renderer/modules/screen_enumeration/screen_enumeration.idl
@@ -10,5 +10,7 @@
 ] partial interface Window {
   [CallWith=ScriptState, RaisesException] Promise<sequence<Screen>> getScreens();
 
+  [CallWith=ScriptState, RaisesException] Promise<boolean> isMultiScreen();
+
   attribute EventHandler onscreenschange;
 };
diff --git a/third_party/blink/renderer/platform/RuntimeEnabledFeatures.md b/third_party/blink/renderer/platform/RuntimeEnabledFeatures.md
index d49533e..fa98c849 100644
--- a/third_party/blink/renderer/platform/RuntimeEnabledFeatures.md
+++ b/third_party/blink/renderer/platform/RuntimeEnabledFeatures.md
@@ -14,7 +14,7 @@
 ```
 The status of the feature controls when it will be enabled in the Blink engine.
 
-| Status Value | Web feature enabled during web tests with content_shell [1] | Web feature enabled as part of web experimental features [2] | Web feature enabled in stable release | Non-web exposed feature enabled through a command line flag [3]
+| Status Value | Web feature enabled during [web tests] with content_shell [1] | Web feature enabled as part of web experimental features [2] | Web feature enabled in stable release | Non-web exposed feature enabled through a command line flag [3]
 |:---:|:---:|:---:|:---:|:---:|
 | <missing\> | No | No | No | Yes |
 | `test` | Yes | No | No | No |
@@ -134,16 +134,17 @@
 **Warning:** You will not be able to change the enabled state of these at runtime as the V8 object templates definitions are created during start up and will not be updated during runtime.
 
 ## Web Tests (JavaScript)
-Test whether a feature is enabled using:
+
+In [web tests], you can test whether a feature is enabled using:
 ```javascript
 internals.runtimeFlags.amazingNewFeatureEnabled
 ```
-This attribute is read only and cannot be changed.
+This attribute is read only and cannot be changed, unless `settable_from_internals: true` is specified for the feature.
 
-**Note:** The `internals` JavaScript API is only available in ContentShell for use by web tests and does not appear in released versions of Chromium.
+**Note:** The `internals` JavaScript API is only available in content_shell for use by web tests and does not appear in Chromium. In content_shell's browser mode, `--expose-internals-for-testing` is needed to have the `internals` JavaScript API.
 
 ### Running Web Tests
-When content_shell is run with `--stable-release-mode` flag, test-only features (ones listed in [runtime_enabled_features.json5] with `test`) are turned off.
+When content_shell is run for web tests with `--stable-release-mode` flag, test-only and experimental features (ones listed in [runtime_enabled_features.json5] with `status: "test"` or `status: "experimental"`) are turned off. The [virtual/stable] suite runs with the flag, which is one of the ways to ensure test coverage of production code path for these features.
 
 ## Generated Files
 [renderer/build/scripts/make_runtime_features.py][make_runtime_features.py] uses [runtime_enabled_features.json5] to generate:
@@ -173,7 +174,7 @@
 https://groups.google.com/a/chromium.org/d/msg/blink-dev/JBakhu5J6Qs/re2LkfEslTAJ
 
 
-
+[web tests]: <https://chromium.googlesource.com/chromium/src/+/master/docs/testing/web_tests.md>
 [supportedPlatforms]: <https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/platform/runtime_enabled_features.json5#36>
 [cssProperties]: <https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/core/css/css_properties.json5>
 [virtual test suite]: <https://chromium.googlesource.com/chromium/src/+/master/docs/testing/web_tests.md#testing-runtime-flags>
@@ -187,3 +188,4 @@
 [runtime_enabled_features.json5]: <https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/platform/runtime_enabled_features.json5>
 [make_internal_runtime_flags.py]: <https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/build/scripts/make_internal_runtime_flags.py>
 [code_generator_v8.py]: <https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/bindings/scripts/code_generator_v8.py>
+[virtual/stable]: <https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/web_tests/VirtualTestSuites;drc=9878f26d52d32871ed1c085444196e5453909eec;l=112>
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 6e9df01..fb5196f 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -171,7 +171,12 @@
   if (display_item.IsGraphicsLayerWrapper()) {
     const auto& graphics_layer_display_item =
         static_cast<const GraphicsLayerDisplayItem&>(display_item);
-    layer = graphics_layer_display_item.GetGraphicsLayer().CcLayer();
+    const auto& graphics_layer = graphics_layer_display_item.GetGraphicsLayer();
+    if (graphics_layer.ShouldCreateLayersAfterPaint()) {
+      DCHECK(RuntimeEnabledFeatures::CompositeSVGEnabled());
+      return nullptr;
+    }
+    layer = graphics_layer.CcLayer();
     layer_offset = FloatPoint(graphics_layer_display_item.GetGraphicsLayer()
                                   .GetOffsetFromTransformNode());
   } else {
@@ -890,6 +895,28 @@
         requires_own_layer = first_display_item.IsForeignLayer() ||
                              first_display_item.IsGraphicsLayerWrapper() ||
                              IsCompositedScrollbar(first_display_item);
+
+        if (first_display_item.IsGraphicsLayerWrapper()) {
+          const GraphicsLayerDisplayItem& graphics_layer_display_item =
+              static_cast<const GraphicsLayerDisplayItem&>(first_display_item);
+          const GraphicsLayer& graphics_layer =
+              graphics_layer_display_item.GetGraphicsLayer();
+          if (graphics_layer.ShouldCreateLayersAfterPaint()) {
+            DCHECK(RuntimeEnabledFeatures::CompositeSVGEnabled());
+            scoped_refptr<const PaintArtifact> sub_paint_artifact =
+                graphics_layer.GetPaintController().GetPaintArtifactShared();
+            Vector<PaintChunk>::const_iterator cursor =
+                sub_paint_artifact->PaintChunks().begin();
+            LayerizeGroup(
+                sub_paint_artifact,
+                graphics_layer.GetPropertyTreeState().Unalias().Effect(),
+                cursor);
+            DCHECK_EQ(sub_paint_artifact->PaintChunks().end(), cursor);
+
+            chunk_it++;
+            continue;
+          }
+        }
       }
       DCHECK(!requires_own_layer || chunk_it->size() <= 1u);
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing_reasons.cc b/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
index 34213a4..d1dcd177 100644
--- a/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
@@ -27,6 +27,7 @@
      "promoted to a layer based on a performance heuristic."},
     {CompositingReason::kPlugin, "plugin", "Is an accelerated plugin"},
     {CompositingReason::kIFrame, "iFrame", "Is an accelerated iFrame"},
+    {CompositingReason::kSVGRoot, "SVGRoot", "Is an accelerated SVG root"},
     {CompositingReason::kBackfaceVisibilityHidden, "backfaceVisibilityHidden",
      "Has backface-visibility: hidden"},
     {CompositingReason::kActiveTransformAnimation, "activeTransformAnimation",
diff --git a/third_party/blink/renderer/platform/graphics/compositing_reasons.h b/third_party/blink/renderer/platform/graphics/compositing_reasons.h
index 3af77d3..e25a258 100644
--- a/third_party/blink/renderer/platform/graphics/compositing_reasons.h
+++ b/third_party/blink/renderer/platform/graphics/compositing_reasons.h
@@ -23,6 +23,7 @@
   V(Canvas)                                                                   \
   V(Plugin)                                                                   \
   V(IFrame)                                                                   \
+  V(SVGRoot)                                                                  \
   V(BackfaceVisibilityHidden)                                                 \
   V(ActiveTransformAnimation)                                                 \
   V(ActiveOpacityAnimation)                                                   \
@@ -124,9 +125,9 @@
         kWillChangeBackdropFilter,
 
     kComboAllDirectNonStyleDeterminedReasons =
-        kVideo | kCanvas | kPlugin | kIFrame | kOverflowScrollingParent |
-        kOutOfFlowClipping | kVideoOverlay | kXrOverlay | kRoot |
-        kRootScroller | kScrollDependentPosition |
+        kVideo | kCanvas | kPlugin | kIFrame | kSVGRoot |
+        kOverflowScrollingParent | kOutOfFlowClipping | kVideoOverlay |
+        kXrOverlay | kRoot | kRootScroller | kScrollDependentPosition |
         kBackfaceInvisibility3DAncestor,
 
     kComboAllDirectReasons = kComboAllDirectStyleDeterminedReasons |
@@ -156,7 +157,8 @@
         kOverlap | kAssumedOverlap | kOverflowScrollingParent,
 
     kDirectReasonsForPaintOffsetTranslationProperty =
-        kScrollDependentPosition | kVideo | kCanvas | kPlugin | kIFrame,
+        kScrollDependentPosition | kVideo | kCanvas | kPlugin | kIFrame |
+        kSVGRoot,
 
     kDirectReasonsForTransformProperty =
         k3DTransform | kTrivial3DTransform | kWillChangeTransform |
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index 822f668c..e911c04 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -72,6 +72,7 @@
       contents_visible_(true),
       hit_testable_(false),
       needs_check_raster_invalidation_(false),
+      should_create_layers_after_paint_(false),
       painting_phase_(kGraphicsLayerPaintAllWithOverflowClip),
       parent_(nullptr),
       raster_invalidation_function_(
@@ -301,6 +302,7 @@
 
   if (PaintWithoutCommit()) {
     GetPaintController().CommitNewDisplayItems();
+    UpdateShouldCreateLayersAfterPaint();
     UpdateSafeOpaqueBackgroundColor();
   } else if (!needs_check_raster_invalidation_) {
     return false;
@@ -315,23 +317,25 @@
 
   DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName();
   // Generate raster invalidations for SPv1.
-  IntRect layer_bounds(layer_state_->offset, IntSize(Size()));
-  EnsureRasterInvalidator().Generate(
-      raster_invalidation_function_,
-      GetPaintController().GetPaintArtifactShared(), layer_bounds,
-      layer_state_->state.Unalias(), VisualRectSubpixelOffset(), this);
+  if (!ShouldCreateLayersAfterPaint()) {
+    IntRect layer_bounds(layer_state_->offset, IntSize(Size()));
+    EnsureRasterInvalidator().Generate(
+        raster_invalidation_function_,
+        GetPaintController().GetPaintArtifactShared(), layer_bounds,
+        layer_state_->state.Unalias(), VisualRectSubpixelOffset(), this);
 
-  if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
-      PaintsContentOrHitTest()) {
-    auto& tracking = EnsureRasterInvalidator().EnsureTracking();
-    tracking.CheckUnderInvalidations(DebugName(), CapturePaintRecord(),
-                                     InterestRect());
-    if (auto record = tracking.UnderInvalidationRecord()) {
-      // Add the under-invalidation overlay onto the painted result.
-      GetPaintController().AppendDebugDrawingAfterCommit(std::move(record),
-                                                         layer_state_->state);
-      // Ensure the compositor will raster the under-invalidation overlay.
-      CcLayer()->SetNeedsDisplay();
+    if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
+        PaintsContentOrHitTest()) {
+      auto& tracking = EnsureRasterInvalidator().EnsureTracking();
+      tracking.CheckUnderInvalidations(DebugName(), CapturePaintRecord(),
+                                       InterestRect());
+      if (auto record = tracking.UnderInvalidationRecord()) {
+        // Add the under-invalidation overlay onto the painted result.
+        GetPaintController().AppendDebugDrawingAfterCommit(std::move(record),
+                                                           layer_state_->state);
+        // Ensure the compositor will raster the under-invalidation overlay.
+        CcLayer()->SetNeedsDisplay();
+      }
     }
   }
 
@@ -347,6 +351,31 @@
           GetPaintController().GetPaintArtifact().PaintChunks()));
 }
 
+void GraphicsLayer::UpdateShouldCreateLayersAfterPaint() {
+  should_create_layers_after_paint_ = false;
+  if (!RuntimeEnabledFeatures::CompositeSVGEnabled())
+    return;
+  if (!PaintsContentOrHitTest())
+    return;
+  // Only layerize content under SVG for now. This requires that the SVG root
+  // has a GraphicsLayer.
+  if (!client_.IsSVGRoot())
+    return;
+  const PaintChunkSubset paint_chunks =
+      PaintChunkSubset(GetPaintController().PaintChunks());
+  for (const auto& paint_chunk : paint_chunks) {
+    const auto& chunk_state = paint_chunk.properties;
+    if (chunk_state.GetPropertyTreeState() == GetPropertyTreeState())
+      continue;
+    // TODO(pdr): Check for direct compositing reasons along the chain from
+    // this state to the layer state.
+    if (chunk_state.HasDirectCompositingReasons()) {
+      should_create_layers_after_paint_ = true;
+      return;
+    }
+  }
+}
+
 bool GraphicsLayer::PaintWithoutCommitForTesting(
     const base::Optional<IntRect>& interest_rect) {
   return PaintWithoutCommit(base::OptionalOrNullptr(interest_rect));
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.h b/third_party/blink/renderer/platform/graphics/graphics_layer.h
index 8180f182..f3f43b4d 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -233,6 +233,10 @@
   bool PaintWithoutCommitForTesting(
       const base::Optional<IntRect>& interest_rect = base::nullopt);
 
+  bool ShouldCreateLayersAfterPaint() const {
+    return should_create_layers_after_paint_;
+  }
+
  protected:
   String DebugName(const cc::Layer*) const;
 
@@ -247,6 +251,7 @@
   bool FillsBoundsCompletely() const override { return false; }
   size_t GetApproximateUnsharedMemoryUsage() const final;
 
+  void UpdateShouldCreateLayersAfterPaint();
   void UpdateSafeOpaqueBackgroundColor();
 
   // Returns true if PaintController::PaintArtifact() changed and needs commit.
@@ -286,6 +291,10 @@
   bool contents_visible_ : 1;
   bool hit_testable_ : 1;
   bool needs_check_raster_invalidation_ : 1;
+  // True if the cc::Layers for this GraphicsLayer should be created after
+  // paint (in PaintArtifactCompositor). This depends on the display item list
+  // and is updated after CommitNewDisplayItems.
+  bool should_create_layers_after_paint_ : 1;
 
   GraphicsLayerPaintingPhase painting_phase_;
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer_client.h b/third_party/blink/renderer/platform/graphics/graphics_layer_client.h
index 0106ae1..c2431f6 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer_client.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer_client.h
@@ -76,6 +76,8 @@
   // painting and hit testing.
   virtual bool IsUnderSVGHiddenContainer() const { return false; }
 
+  virtual bool IsSVGRoot() const { return false; }
+
   virtual bool IsTrackingRasterInvalidations() const { return false; }
 
   virtual void GraphicsLayersDidChange() {}
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item.h b/third_party/blink/renderer/platform/graphics/paint/display_item.h
index 25b6019e..74f6b6b 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item.h
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item.h
@@ -215,12 +215,6 @@
     return static_cast<RasterEffectOutset>(raster_effect_outset_);
   }
 
-  // Visual rect can change without needing invalidation of the client, e.g.
-  // when ancestor clip changes. This is called during under invalidation
-  // checking. TODO(crbug.com/1104064): Remove this when we can compute more
-  // accurate visual rects for such display items.
-  void SetVisualRect(const IntRect& visual_rect) { visual_rect_ = visual_rect; }
-
   Type GetType() const { return static_cast<Type>(type_); }
 
   // Size of this object in memory, used to move it with memcpy.
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
index e28df5b..250ab66e 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
@@ -757,14 +757,6 @@
       old_item_index < current_paint_artifact_->GetDisplayItemList().size()
           ? &current_paint_artifact_->GetDisplayItemList()[old_item_index]
           : nullptr;
-  // TODO(crbug.com/1104064): Temporarily disable visual rect comparison in
-  // under invalidation checking. For now different visual rect happens for
-  // LayoutSVGContainer's mask or clip path display item which doesn't change
-  // and doesn't need repaint while the LayoutSVGContainer's visual rect changes
-  // with descendants. Will remove this when we can compute more accurate visual
-  // rect for these display items.
-  if (old_item)
-    new_item.SetVisualRect(old_item->VisualRect());
 
   if (!old_item || !new_item.Equals(*old_item)) {
     // If we ever skipped reporting any under-invalidations, report the earliest
diff --git a/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h b/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h
index 7ac50524..87608c5 100644
--- a/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h
+++ b/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h
@@ -26,7 +26,10 @@
     return *this = RefCountedPropertyTreeState(property_tree_state);
   }
 
-  bool HasDirectCompositingReasons() const;
+  bool HasDirectCompositingReasons() const {
+    return Transform().Unalias().HasDirectCompositingReasons() ||
+           Effect().Unalias().HasDirectCompositingReasons();
+  }
 
   const TransformPaintPropertyNodeOrAlias& Transform() const {
     return *transform_;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index b04a79c..a7c53b9 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -322,6 +322,9 @@
       status: {"Android": "stable"},
     },
     {
+      name: "CompositeSVG",
+    },
+    {
       name: "ComputedAccessibilityInfo",
       status: "experimental",
     },
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 39c47dc..9400811 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -98,6 +98,9 @@
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-006.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-007.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-008.tentative.html [ Failure ]
+crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-009.tentative.html [ Failure ]
+crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-010.tentative.html [ Failure ]
+crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-011.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/auto-margins-001.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-001.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-002.tentative.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index e75bb343..5ec7e1d 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -262,16 +262,8 @@
 
 # not implemented yet
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-020.tentative.html [ Failure ]
-
-# abspos is not implemented yet for aspect-ratio
-crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-003.tentative.html [ Failure ]
-crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-005.tentative.html [ Failure ]
-crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-006.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-008.tentative.html [ Failure ]
 
-# It is not clear if this test is correct. https://github.com/w3c/csswg-drafts/issues/5151
-crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/abspos-004.tentative.html [ Failure ]
-
 # Incorrect blending with foreignObject and svg descendants.
 crbug.com/1102803 external/wpt/svg/extensibility/foreignObject/isolation-with-svg.html [ Failure ]
 
diff --git a/third_party/blink/web_tests/css3/blending/svg-blend-layer-blend.html b/third_party/blink/web_tests/css3/blending/svg-blend-layer-blend.html
index 53c77d5..0576b71 100644
--- a/third_party/blink/web_tests/css3/blending/svg-blend-layer-blend.html
+++ b/third_party/blink/web_tests/css3/blending/svg-blend-layer-blend.html
@@ -2,7 +2,8 @@
 <html>
 <body>
 <p>Test for isolation caused by blending. This test passes if the whole rectangle is green.</p>
-<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="400px" height="400px" >
+<!-- The "contain: paint" style is a workaround to avoid subpixel differences in the text above. -->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="400px" height="400px" style="contain: paint" >
 <g transform="scale(4 4)">
   <rect x="0" y="0" width="40" height="40" fill="rgb(0,255,0)"/>
   <g style="mix-blend-mode: multiply">
diff --git a/third_party/blink/web_tests/css3/blending/svg-isolation-nested-svg-no-isolation.html b/third_party/blink/web_tests/css3/blending/svg-isolation-nested-svg-no-isolation.html
index d2d613f..787f8b1ed 100644
--- a/third_party/blink/web_tests/css3/blending/svg-isolation-nested-svg-no-isolation.html
+++ b/third_party/blink/web_tests/css3/blending/svg-isolation-nested-svg-no-isolation.html
@@ -2,7 +2,8 @@
 <html>
     <body>
         <p>Nested svg should not isolate. The test passes if you see a lime square.</p>
-        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="200px">
+        <!-- The "contain: paint" style is a workaround to avoid subpixel differences in the text above. -->
+        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="200px" style="contain: paint;" >
             <rect x="0" y="0" width="200" height="200" style="fill: yellow"/>
             <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="200px">
                 <rect x="0" y="0" width="200" height="200" style="fill: red; mix-blend-mode: difference"/>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 767c967..79dc635 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -156392,6 +156392,10 @@
      "ebf84dd43fe17d345f7db7cd736470d403ff6c99",
      []
     ],
+    "idlharness.https.window-expected.txt": [
+     "dc584f8aaf9acb578dcb499b10bd95a10f362898",
+     []
+    ],
     "setSinkId.https-expected.txt": [
      "81cdb58c5f45395a51395571ea2cd7979faf8d7c",
      []
@@ -198938,10 +198942,6 @@
       []
      ]
     },
-    "idlharness.https-expected.txt": [
-     "eb01b2e0909cf2db745f4ef7417f9d826022c077",
-     []
-    ],
     "polyfill": {
      "cast-polyfill.js": [
       "576e0ad04066d37670634b39fc128dc2ea905dae",
@@ -212171,6 +212171,10 @@
         "ed973f09e82a6d91819ef60438018a096b406f32",
         []
        ],
+       "image-loading-lazy-expected.txt": [
+        "724ecac7ecfd4ce12ff9faf48d58d6c79ef61472",
+        []
+       ],
        "image-loading-lazy-move-document-expected.txt": [
         "19b93ec6269d92ec8ecee15adc11d1947239dd27",
         []
@@ -216145,7 +216149,7 @@
      []
     ],
     "audio-output.idl": [
-     "cf0f46d08bc3dafae5f62cc505b3b3791ad3f7da",
+     "2041e6ea8fdb5d2f67cf9596a71de4b290bee869",
      []
     ],
     "background-fetch.idl": [
@@ -216637,7 +216641,7 @@
      []
     ],
     "streams.idl": [
-     "35f558f624a83f3e2526e89b9be909a7883ecb08",
+     "3d7484a20e0553daba3cc336ba1b61c233e35e68",
      []
     ],
     "text-detection-api.tentative.idl": [
@@ -216657,7 +216661,7 @@
      []
     ],
     "uievents.idl": [
-     "b00f7badb90b307c45c86e15b4213d91dcc088de",
+     "67405ebf118b4b584d9cc335918bc45eb3e5907b",
      []
     ],
     "url.idl": [
@@ -216681,7 +216685,7 @@
      []
     ],
     "wai-aria.idl": [
-     "d5d8f32521a3b22647eece7a7662861b6da9e060",
+     "5b1bacc932ffdafffd6869d50146fb467ffc168a",
      []
     ],
     "wake-lock.idl": [
@@ -222819,7 +222823,7 @@
      ]
     },
     "idlharness.js": [
-     "25c193b55e5b76fff6dc180268357a11a27abc08",
+     "9a23e3a1da203309150e80ac820aa3df294ae3c3",
      []
     ],
     "idlharness.js.headers": [
@@ -227944,7 +227948,7 @@
       []
      ],
      "epochs_update.sh": [
-      "4b393fa6db5f90c18daccf6cef3a9706f1411415",
+      "f460814c962076b4ec72c072dff09925427c90a6",
       []
      ],
      "jobs.py": [
@@ -228108,11 +228112,11 @@
       []
      ],
      "lint.py": [
-      "14052e7a9723ce52cc3ffd64bc6ef454e834f751",
+      "ad053dcca584cd2616e62dd8b414192efeb5f9d8",
       []
      ],
      "rules.py": [
-      "6ffd749b9ad539b56ab93f4cd3dfffe8ed324aa1",
+      "c67745792d1bec367fe6e2faa28d725fdedaf1fc",
       []
      ]
     },
@@ -234565,7 +234569,7 @@
        []
       ],
       "server.py": [
-       "c2498c4ee1e8f45de604f71394c3d6feef82aef1",
+       "56342ed287d8fa1028796e5083e1f1f99375217a",
        []
       ],
       "sslutils": {
@@ -236477,6 +236481,12 @@
        ]
       }
      },
+     "the-audiocontext-interface": {
+      "constructor-allowed-to-start-expected.txt": [
+       "f7801aceefcb51a24ffe4be09b804f7794154167",
+       []
+      ]
+     },
      "the-audioparam-interface": {
       "automation-rate-testing.js": [
        "43279f91d68d3c5e2d7a086c739c838b31dc7335",
@@ -254389,7 +254399,7 @@
    },
    "audio-output": {
     "idlharness.https.window.js": [
-     "c13b167296d8059b96bfa4704714051e341ee987",
+     "d7cdbd076833481b53f35b26d5ee016fd70c4111",
      [
       "audio-output/idlharness.https.window.html",
       {
@@ -274229,6 +274239,13 @@
         {}
        ]
       ],
+      "grid-item-flex-container-001.html": [
+       "56c999c55882dabb78c35436fa7f9ac890e6901f",
+       [
+        null,
+        {}
+       ]
+      ],
       "grid-item-min-auto-size-001.html": [
        "f50e9ef312418f4d3b737bd55b4a7e5c75f09230",
        [
@@ -342349,13 +342366,6 @@
          {}
         ]
        ],
-       "image-loading-lazy-load-event.html": [
-        "0da5379df48a20f16374774c989fa459dbe18ee5",
-        [
-         null,
-         {}
-        ]
-       ],
        "image-loading-lazy-move-document.html": [
         "ff7e83105cc904145e67eb156653b69b6f0b113a",
         [
@@ -342413,7 +342423,7 @@
         ]
        ],
        "image-loading-lazy.html": [
-        "0c1c39a8ae95f0b978f3b2a3ebd822598295372b",
+        "35f25f8a15455ec3e504c67f343112c088aee872",
         [
          null,
          {}
@@ -398300,6 +398310,15 @@
         {}
        ]
       ],
+      "constructor-allowed-to-start.html": [
+       "f866b5f7a1cb408c1a0c26c21c99bb7a28d53e82",
+       [
+        null,
+        {
+         "testdriver": true
+        }
+       ]
+      ],
       "processing-after-resume.https.html": [
        "e000ab124fefa6f0eea4e5517d04436428c0cd8c",
        [
diff --git a/third_party/blink/web_tests/external/wpt/audio-output/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/audio-output/idlharness.https.window-expected.txt
new file mode 100644
index 0000000..dc584f8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/audio-output/idlharness.https.window-expected.txt
@@ -0,0 +1,22 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS idl_test validation
+PASS Partial interface HTMLMediaElement: original interface defined
+PASS Partial interface HTMLMediaElement: member names are unique
+PASS Partial interface MediaDevices: original interface defined
+PASS Partial interface MediaDevices: member names are unique
+PASS Partial interface MediaDevices[2]: member names are unique
+PASS HTMLElement includes GlobalEventHandlers: member names are unique
+PASS HTMLElement includes DocumentAndElementEventHandlers: member names are unique
+PASS HTMLElement includes ElementContentEditable: member names are unique
+PASS HTMLElement includes HTMLOrSVGElement: member names are unique
+PASS Element includes ParentNode: member names are unique
+PASS Element includes NonDocumentTypeChildNode: member names are unique
+PASS Element includes ChildNode: member names are unique
+PASS Element includes Slottable: member names are unique
+FAIL MediaDevices interface: operation selectAudioOutput() assert_own_property: interface prototype object missing non-static operation expected property "selectAudioOutput" missing
+FAIL MediaDevices interface: navigator.mediaDevices must inherit property "selectAudioOutput()" with the proper type assert_inherits: property "selectAudioOutput" not found in prototype chain
+PASS HTMLMediaElement interface: attribute sinkId
+PASS HTMLMediaElement interface: operation setSinkId(DOMString)
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/audio-output/idlharness.https.window.js b/third_party/blink/web_tests/external/wpt/audio-output/idlharness.https.window.js
index c13b167..d7cdbd0 100644
--- a/third_party/blink/web_tests/external/wpt/audio-output/idlharness.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/audio-output/idlharness.https.window.js
@@ -7,13 +7,14 @@
 
 idl_test(
   ['audio-output'],
-  ['html', 'dom'],
+  ['mediacapture-streams', 'html', 'dom'],
   idl_array => {
     self.audio = document.createElement('audio');
     self.video = document.createElement('video');
     idl_array.add_objects({
       HTMLAudioElement: ['audio'],
-      HTMLVideoElement: ['video']
+      HTMLVideoElement: ['video'],
+      MediaDevices: ['navigator.mediaDevices'],
     });
   }
 );
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/abspos-009.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/abspos-009.tentative.html
new file mode 100644
index 0000000..0e71de2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/abspos-009.tentative.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>CSS aspect-ratio: abspos div inline size with percentage height</title>
+<link rel="author" title="Google LLC" href="https://www.google.com/">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<div style="width: 500px; position: relative;">
+  <div style="background: green; aspect-ratio: 1/1; height: 100%; position: absolute; left: 0; right: 0; top: 0; bottom: 0;"></div>
+  <div style="height: 100px"></div> <!-- for sizing the abspos containing block -->
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/abspos-010.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/abspos-010.tentative.html
new file mode 100644
index 0000000..1d7ddec
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/abspos-010.tentative.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>CSS aspect-ratio: out-of-flow div block size with box-sizing</title>
+<link rel="author" title="Google LLC" href="https://www.google.com/">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<div style="background: green; width: 100px; padding: 10px 20px 30px 40px; aspect-ratio: 1/1; position: absolute; box-sizing: border-box;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/abspos-011.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/abspos-011.tentative.html
new file mode 100644
index 0000000..01fac9d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/abspos-011.tentative.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>CSS aspect-ratio: out-of-flow div block size with box-sizing</title>
+<link rel="author" title="Google LLC" href="https://www.google.com/">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<div style="background: green; width: 80px; padding: 0px 5px 0px 15px; aspect-ratio: 1/1; position: absolute; box-sizing: content-box;"></div>
+<div style="height: 80px;"></div>
+<div style="background: green; width: 100px; height: 20px;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/pitch-detector.js b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/pitch-detector.js
index 375fef7..78f22cc 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/pitch-detector.js
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/pitch-detector.js
@@ -4,11 +4,14 @@
 
 var FFT_SIZE = 2048;
 
-function getPitchDetector(media, t) {
-  var audioContext = new AudioContext();
-  t.add_cleanup(() => audioContext.close());
+var audioContext;
+var sourceNode;
 
-  var sourceNode = audioContext.createMediaElementSource(media);
+function getPitchDetector(media) {
+  if(!audioContext) {
+    audioContext = new AudioContext();
+    sourceNode = audioContext.createMediaElementSource(media);
+  }
 
   var analyser = audioContext.createAnalyser();
   analyser.fftSize = FFT_SIZE;
@@ -16,14 +19,22 @@
   sourceNode.connect(analyser);
   analyser.connect(audioContext.destination);
 
-  // Returns the frequency value for the nth FFT bin.
-  var binConverter = (bin) => audioContext.sampleRate*(bin/FFT_SIZE);
-
-  return () => getPitch(analyser, binConverter);
+  return {
+      ensureStart() { return audioContext.resume(); },
+      detect() { return getPitch(analyser); },
+      cleanup() {
+        sourceNode.disconnect();
+        analyser.disconnect();
+      },
+  };
 }
 
-function getPitch(analyser, binConverter) {
-  var buf = new Uint8Array(FFT_SIZE/2);
+function getPitch(analyser) {
+  // Returns the frequency value for the nth FFT bin.
+  var binConverter = (bin) =>
+    (audioContext.sampleRate/2)*((bin)/(analyser.frequencyBinCount-1));
+
+  var buf = new Uint8Array(analyser.frequencyBinCount);
   analyser.getByteFrequencyData(buf);
   return findDominantFrequency(buf, binConverter);
 }
@@ -40,9 +51,8 @@
     }
   }
 
-  // The distance between bins is always constant and corresponds to
-  // (1/FFT_SIZE)th of the sample rate. Use the frequency value of the 1st bin
-  // as the margin directly, instead of calculating an average from the values
-  // of the neighboring bins.
+  // The spread of frequencies within bins is constant and corresponds to
+  // (1/(FFT_SIZE-1))th of the sample rate. Use the value of bin #1 as a
+  // shorthand for that value.
   return { value:binConverter(bin), margin:binConverter(1) };
 }
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/preserves-pitch.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/preserves-pitch.html
index dad5f94..1cf6c76 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/preserves-pitch.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/media-elements/preserves-pitch.html
@@ -15,8 +15,8 @@
     if ("mozPreservesPitch" in HTMLAudioElement.prototype) {
         return audio.mozPreservesPitch;
     }
-    if ("wekbitPreservesPitch" in HTMLAudioElement.prototype) {
-        return audio.wekbitPreservesPitch;
+    if ("webkitPreservesPitch" in HTMLAudioElement.prototype) {
+        return audio.webkitPreservesPitch;
     }
     return undefined;
 }
@@ -27,8 +27,8 @@
         audio.preservesPitch = value;
     } else if ("mozPreservesPitch" in HTMLAudioElement.prototype) {
         audio.mozPreservesPitch = value;
-    } else if ("wekbitPreservesPitch" in HTMLAudioElement.prototype) {
-        audio.wekbitPreservesPitch = value;
+    } else if ("webkitPreservesPitch" in HTMLAudioElement.prototype) {
+        audio.webkitPreservesPitch = value;
     }
 }
 
@@ -37,51 +37,82 @@
 }, "Test that preservesPitch is present and unprefixed.");
 
 test(function(t) {
-    let audio = document.createElement('audio');
-    assert_true(getPreservesPitch(audio));
+    let defaultAudio = document.createElement('audio');
+    assert_true(getPreservesPitch(defaultAudio));
 
-    setPreservesPitch(audio, false);
-    assert_false(getPreservesPitch(audio));
-}, "Test that presevesPitch is on by default");
+    setPreservesPitch(defaultAudio, false);
+    assert_false(getPreservesPitch(defaultAudio));
+}, "Test that preservesPitch is on by default");
+
+
+var audio;
+
+function addTestCleanups(t, detector) {
+    t.add_cleanup(() => {
+        audio.pause();
+        audio.currentTime = 0;
+    });
+    t.add_cleanup(() => detector.cleanup());
+}
 
 function testPreservesPitch(preservesPitch, playbackRate, expectedPitch, description) {
     promise_test(async t => {
-        let audio = document.createElement('audio');
-
-        var detector = getPitchDetector(audio, t);
-
-        // This file contains 5 seconds of a 440hz sine wave.
-        audio.src = "/media/sine440.mp3";
+        let detector = getPitchDetector(audio);
+        addTestCleanups(t, detector);
 
         audio.playbackRate = playbackRate;
         setPreservesPitch(audio, preservesPitch);
 
-        function promiseTimeUpdate() {
-            return new Promise((resolve) => audio.ontimeupdate = resolve);
+        function waitUntil(time) {
+            return new Promise((resolve) => {
+                audio.ontimeupdate = () => {
+                    if (audio.currentTime >= time) {
+                        resolve();
+                    }
+                };
+            });
         }
 
-        function verifyPitch() {
-            var pitch = detector();
+        // Wait until we have played some audio. Otherwise, the detector
+        // might return a pitch of 0Hz.
+        audio.play();
+        await waitUntil(0.25);
 
-            // 25Hz is larger than the margin we get from 48kHz and 44.1kHz
-            // audio being analyzed by a FFT of size 2048. If we get something
-            // different, there is an error within the test's calculations (or
-            // we might be dealing a larger sample rate).
-            assert_less_than(pitch.margin, 25,
-                "Test error: the margin should be reasonably small.")
+        var pitch = detector.detect();
 
-            assert_approx_equals(pitch.value, expectedPitch, pitch.margin,
-                "The actual pitch should be close to the expected pitch.");
-        }
+        // 25Hz is larger than the margin we get from 48kHz and 44.1kHz
+        // audio being analyzed by a FFT of size 2048. If we get something
+        // different, there is an error within the test's calculations (or
+        // we might be dealing a larger sample rate).
+        assert_less_than(pitch.margin, 25,
+            "Test error: the margin should be reasonably small.")
 
-        await test_driver.bless("Play audio element", () => audio.play() )
-          .then(promiseTimeUpdate)
-          .then(verifyPitch);
+        assert_approx_equals(pitch.value, expectedPitch, pitch.margin,
+            "The actual pitch should be close to the expected pitch.");
+
     }, description);
 }
 
 var REFERENCE_PITCH = 440;
 
+promise_test(async t => {
+    // Create the audio element only once, in order to lower the chances of
+    // tests timing out.
+    audio = document.createElement('audio');
+
+    // This file contains 5 seconds of a 440hz sine wave.
+    audio.src = "/media/sine440.mp3";
+
+    let detector = getPitchDetector(audio);
+    addTestCleanups(t, detector);
+
+    // The first time we run the test, we need to interact with the
+    // AudioContext and Audio element via user gestures.
+    await test_driver.bless("Play audio element", () => {
+        return Promise.all([audio.play(), detector.ensureStart()]);
+    });
+}, "Setup Audio element and AudioContext")
+
 testPreservesPitch(true, 1.0, REFERENCE_PITCH,
     "The default playbackRate should not affect pitch")
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-expected.txt
index 885afb6..fb88049 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-expected.txt
@@ -1,7 +1,8 @@
 This is a testharness.js-based test.
 PASS In-viewport iframes load eagerly
-PASS In-viewport srcdoc iframes load eagerly
 PASS Below-viewport iframes load lazily
 FAIL Below-viewport srcdoc iframes load lazily assert_true: The window load event should have fired before the below-viewport srcdoc iframe's subresource loads expected true got false
+FAIL Below-viewport data: url iframes load lazily assert_true: The window load event should have fired before the below-viewport data url iframe loads expected true got false
+FAIL Below-viewport blob url iframes load lazily assert_true: The window load event should have fired before the below-viewport blob url iframe loads expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy.html
index 51b4426..336703e 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy.html
@@ -11,12 +11,22 @@
 <script>
   const t_in_viewport =
     async_test('In-viewport iframes load eagerly');
-  const t_in_viewport_srcdoc=
-    async_test('In-viewport srcdoc iframes load eagerly');
   const t_below_viewport =
     async_test('Below-viewport iframes load lazily');
   const t_below_viewport_srcdoc =
     async_test('Below-viewport srcdoc iframes load lazily');
+  const t_below_viewport_data_url =
+    async_test('Below-viewport data: url iframes load lazily');
+  const t_below_viewport_blob_url =
+    async_test('Below-viewport blob url iframes load lazily');
+
+
+  document.addEventListener('DOMContentLoaded', e => {
+    const blob_url_iframe = document.querySelector('#below_viewport_blob_url');
+    const blob = new Blob(['<p>Blob subframe</p>'], {type: 'text/html'});
+    const url = URL.createObjectURL(blob);
+    blob_url_iframe.src = url;
+  });
 
   let has_window_loaded = false;
 
@@ -25,11 +35,6 @@
       "The in_viewport iframe should not block the load event");
   });
 
-  const in_viewport_srcdoc_iframe_onload = t_in_viewport_srcdoc.step_func_done(() => {
-    assert_false(has_window_loaded,
-      "The in_viewport srcdoc iframe should not block the load event");
-  });
-
   window.addEventListener("load", () => {
     has_window_loaded = true;
     document.getElementById("below_viewport_srcdoc").scrollIntoView();
@@ -41,6 +46,7 @@
                 "the below-viewport iframe loads");
   });
 
+  // Onload handlers for below-viewport srcdoc iframe.
   // Must make this accessible to the srcdoc iframe's body.
   window.below_viewport_srcdoc_iframe_subresource_onload = t_below_viewport_srcdoc.step_func(() => {
     assert_true(has_window_loaded,
@@ -53,16 +59,27 @@
                 "The window load event should have fired before " +
                 "the below-viewport srcdoc iframe loads");
   });
+
+  // Onload handler for below-viewport data url iframe.
+  const below_viewport_data_url_iframe_onload = t_below_viewport_data_url.step_func_done(() => {
+    assert_true(has_window_loaded,
+                "The window load event should have fired before " +
+                "the below-viewport data url iframe loads");
+  });
+
+  // Onload handler for below-viewport blob url iframe.
+  const below_viewport_blob_url_iframe_onload = t_below_viewport_blob_url.step_func_done(() => {
+    assert_true(has_window_loaded,
+                "The window load event should have fired before " +
+                "the below-viewport blob url iframe loads");
+  });
+
 </script>
 
 <body>
   <iframe id="in_viewport" src="resources/subframe.html?in-viewport"
           loading="lazy" width="200px" height="100px"
           onload="in_viewport_iframe_onload();"></iframe>
-  <iframe id="in_viewport_srcdoc"
-          srcdoc="<body><img src='/common/square.png?in-viewport'></body>"
-          loading="lazy" width="200px" height="100px"
-          onload="in_viewport_srcdoc_iframe_onload();"></iframe>
 
   <div style="height:2000vh;"></div>
   <iframe id="below_viewport" src="resources/subframe.html?below-viewport"
@@ -73,6 +90,15 @@
                   onload='parent.below_viewport_srcdoc_iframe_subresource_onload();'></body>"
           loading="lazy" width="200px" height="100px"
           onload="below_viewport_srcdoc_iframe_onload();"></iframe>
+  <iframe id="below_viewport_data_url"
+          src="data:text/html,<p>Subframe</p>"
+          loading="lazy" width="200px" height="100px"
+          onload="below_viewport_data_url_iframe_onload();"></iframe>
+  <!-- This iframe has its `src` set to a blob URL dynamically above -->
+  <iframe id="below_viewport_blob_url"
+          loading="lazy" width="200px" height="100px"
+          onload="below_viewport_blob_url_iframe_onload();"></iframe>
+
 
 
   <!-- This async script loads very slowly in order to ensure that, if the
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/audio-output.idl b/third_party/blink/web_tests/external/wpt/interfaces/audio-output.idl
index cf0f46d0..2041e6e 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/audio-output.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/audio-output.idl
@@ -7,3 +7,7 @@
   [SecureContext] readonly attribute DOMString sinkId;
   [SecureContext] Promise<void> setSinkId (DOMString sinkId);
 };
+
+partial interface MediaDevices {
+  Promise<MediaDeviceInfo> selectAudioOutput();
+};
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/streams.idl b/third_party/blink/web_tests/external/wpt/interfaces/streams.idl
index 35f558f..3d7484a 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/streams.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/streams.idl
@@ -137,7 +137,7 @@
 };
 
 callback UnderlyingSinkStartCallback = any (WritableStreamDefaultController controller);
-callback UnderlyingSinkWriteCallback = Promise<void> (WritableStreamDefaultController controller, optional any chunk);
+callback UnderlyingSinkWriteCallback = Promise<void> (any chunk, WritableStreamDefaultController controller);
 callback UnderlyingSinkCloseCallback = Promise<void> ();
 callback UnderlyingSinkAbortCallback = Promise<void> (optional any reason);
 
@@ -180,7 +180,7 @@
 
 callback TransformerStartCallback = any (TransformStreamDefaultController controller);
 callback TransformerFlushCallback = Promise<void> (TransformStreamDefaultController controller);
-callback TransformerTransformCallback = Promise<void> (TransformStreamDefaultController controller, optional any chunk);
+callback TransformerTransformCallback = Promise<void> (any chunk, TransformStreamDefaultController controller);
 
 [Exposed=(Window,Worker,Worklet)]
 interface TransformStreamDefaultController {
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/uievents.idl b/third_party/blink/web_tests/external/wpt/interfaces/uievents.idl
index b00f7ba..67405eb 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/uievents.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/uievents.idl
@@ -3,8 +3,9 @@
 // (https://github.com/tidoust/reffy-reports)
 // Source: UI Events (https://w3c.github.io/uievents/)
 
-[Constructor(DOMString type, optional UIEventInit eventInitDict), Exposed=Window]
+[Exposed=Window]
 interface UIEvent : Event {
+  constructor(DOMString type, optional UIEventInit eventInitDict);
   readonly attribute Window? view;
   readonly attribute long detail;
 };
@@ -14,8 +15,9 @@
   long detail = 0;
 };
 
-[Constructor(DOMString type, optional FocusEventInit eventInitDict), Exposed=Window]
+[Exposed=Window]
 interface FocusEvent : UIEvent {
+  constructor(DOMString type, optional FocusEventInit eventInitDict);
   readonly attribute EventTarget? relatedTarget;
 };
 
@@ -23,8 +25,9 @@
   EventTarget? relatedTarget = null;
 };
 
-[Constructor(DOMString type, optional MouseEventInit eventInitDict), Exposed=Window]
+[Exposed=Window]
 interface MouseEvent : UIEvent {
+  constructor(DOMString type, optional MouseEventInit eventInitDict);
   readonly attribute long screenX;
   readonly attribute long screenY;
   readonly attribute long clientX;
@@ -72,8 +75,9 @@
   boolean modifierSymbolLock = false;
 };
 
-[Constructor(DOMString type, optional WheelEventInit eventInitDict), Exposed=Window]
+[Exposed=Window]
 interface WheelEvent : MouseEvent {
+  constructor(DOMString type, optional WheelEventInit eventInitDict);
   // DeltaModeCode
   const unsigned long DOM_DELTA_PIXEL = 0x00;
   const unsigned long DOM_DELTA_LINE  = 0x01;
@@ -92,8 +96,9 @@
   unsigned long deltaMode = 0;
 };
 
-[Constructor(DOMString type, optional InputEventInit eventInitDict), Exposed=Window]
+[Exposed=Window]
 interface InputEvent : UIEvent {
+  constructor(DOMString type, optional InputEventInit eventInitDict);
   readonly attribute DOMString? data;
   readonly attribute boolean isComposing;
   readonly attribute DOMString inputType;
@@ -105,8 +110,9 @@
   DOMString inputType = "";
 };
 
-[Constructor(DOMString type, optional KeyboardEventInit eventInitDict), Exposed=Window]
+[Exposed=Window]
 interface KeyboardEvent : UIEvent {
+  constructor(DOMString type, optional KeyboardEventInit eventInitDict);
   // KeyLocationCode
   const unsigned long DOM_KEY_LOCATION_STANDARD = 0x00;
   const unsigned long DOM_KEY_LOCATION_LEFT = 0x01;
@@ -136,8 +142,9 @@
   boolean isComposing = false;
 };
 
-[Constructor(DOMString type, optional CompositionEventInit eventInitDict), Exposed=Window]
+[Exposed=Window]
 interface CompositionEvent : UIEvent {
+  constructor(DOMString type, optional CompositionEventInit eventInitDict);
   readonly attribute DOMString data;
 };
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl b/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl
index d5d8f32..5b1bacc 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl
@@ -10,53 +10,53 @@
 
 interface mixin AriaAttributes {
 
-  attribute DOMString? ariaAtomic;
-  attribute DOMString? ariaAutoComplete;
-  attribute DOMString? ariaBusy;
-  attribute DOMString? ariaChecked;
-  attribute DOMString? ariaColCount;
-  attribute DOMString? ariaColIndex;
-  attribute DOMString? ariaColIndexText;
-  attribute DOMString? ariaColSpan;
+  attribute DOMString ariaAtomic;
+  attribute DOMString ariaAutoComplete;
+  attribute DOMString ariaBusy;
+  attribute DOMString ariaChecked;
+  attribute DOMString ariaColCount;
+  attribute DOMString ariaColIndex;
+  attribute DOMString ariaColIndexText;
+  attribute DOMString ariaColSpan;
 
-  attribute DOMString? ariaCurrent;
+  attribute DOMString ariaCurrent;
 
-  attribute DOMString? ariaDescription;
+  attribute DOMString ariaDescription;
 
-  attribute DOMString? ariaDisabled;
+  attribute DOMString ariaDisabled;
 
-  attribute DOMString? ariaExpanded;
+  attribute DOMString ariaExpanded;
 
-  attribute DOMString? ariaHasPopup;
-  attribute DOMString? ariaHidden;
-  attribute DOMString? ariaInvalid;
-  attribute DOMString? ariaKeyShortcuts;
-  attribute DOMString? ariaLabel;
+  attribute DOMString ariaHasPopup;
+  attribute DOMString ariaHidden;
+  attribute DOMString ariaInvalid;
+  attribute DOMString ariaKeyShortcuts;
+  attribute DOMString ariaLabel;
 
-  attribute DOMString? ariaLevel;
-  attribute DOMString? ariaLive;
-  attribute DOMString? ariaModal;
-  attribute DOMString? ariaMultiLine;
-  attribute DOMString? ariaMultiSelectable;
-  attribute DOMString? ariaOrientation;
+  attribute DOMString ariaLevel;
+  attribute DOMString ariaLive;
+  attribute DOMString ariaModal;
+  attribute DOMString ariaMultiLine;
+  attribute DOMString ariaMultiSelectable;
+  attribute DOMString ariaOrientation;
 
-  attribute DOMString? ariaPlaceholder;
-  attribute DOMString? ariaPosInSet;
-  attribute DOMString? ariaPressed;
-  attribute DOMString? ariaReadOnly;
-  attribute DOMString? ariaRelevant;
-  attribute DOMString? ariaRequired;
-  attribute DOMString? ariaRoleDescription;
-  attribute DOMString? ariaRowCount;
-  attribute DOMString? ariaRowIndex;
-  attribute DOMString? ariaRowIndexText;
-  attribute DOMString? ariaRowSpan;
-  attribute DOMString? ariaSelected;
-  attribute DOMString? ariaSetSize;
-  attribute DOMString? ariaSort;
-  attribute DOMString? ariaValueMax;
-  attribute DOMString? ariaValueMin;
-  attribute DOMString? ariaValueNow;
-  attribute DOMString? ariaValueText;
+  attribute DOMString ariaPlaceholder;
+  attribute DOMString ariaPosInSet;
+  attribute DOMString ariaPressed;
+  attribute DOMString ariaReadOnly;
+
+  attribute DOMString ariaRequired;
+  attribute DOMString ariaRoleDescription;
+  attribute DOMString ariaRowCount;
+  attribute DOMString ariaRowIndex;
+  attribute DOMString ariaRowIndexText;
+  attribute DOMString ariaRowSpan;
+  attribute DOMString ariaSelected;
+  attribute DOMString ariaSetSize;
+  attribute DOMString ariaSort;
+  attribute DOMString ariaValueMax;
+  attribute DOMString ariaValueMin;
+  attribute DOMString ariaValueNow;
+  attribute DOMString ariaValueText;
 };
 Element includes AriaAttributes;
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/mock-screenenumeration.js b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-screenenumeration.js
index 5a2d7b1..2e41f59 100644
--- a/third_party/blink/web_tests/external/wpt/resources/chromium/mock-screenenumeration.js
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-screenenumeration.js
@@ -44,11 +44,22 @@
     }
 
     async getDisplays() {
+      if (!this.success_)
+        return Promise.resolve({ result: undefined, });
+      let value = new blink.mojom.Displays();
+      value.displays = this.displays_;
+      value.internalId = this.internalId_;
+      value.primaryId = this.primaryId_;
+      return Promise.resolve({ result: value, });
+    }
+
+    async hasMultipleDisplays() {
+      if (!this.success_)
+        return Promise.resolve({ result: blink.mojom.MultipleDisplays.kError });
       return Promise.resolve({
-        displays: this.displays_,
-        internalId: this.internalId_,
-        primaryId: this.primaryId_,
-        success: this.success_,
+        result: this.displays_.length > 1
+            ? blink.mojom.MultipleDisplays.kTrue
+            : blink.mojom.MultipleDisplays.kFalse,
       });
     }
   }
diff --git a/third_party/blink/web_tests/external/wpt/screen_enumeration/README.md b/third_party/blink/web_tests/external/wpt/screen_enumeration/README.md
index e3de83d..42a5ebf 100644
--- a/third_party/blink/web_tests/external/wpt/screen_enumeration/README.md
+++ b/third_party/blink/web_tests/external/wpt/screen_enumeration/README.md
@@ -19,6 +19,7 @@
     addDisplay(display); // Push display to the display vector.
     removeDisplay(id); // Remove display from the display vector.
     async getDisplays(); // Interceptor of getDisplays (screen_enumeration.mojom).
+    async hasMultipleDisplays(); // Interceptor of hasMultipleDisplays (screen_enumeration.mojom).
   };
 ```
 
diff --git a/third_party/blink/web_tests/external/wpt/screen_enumeration/getScreens.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/screen_enumeration/getScreens.tentative.https.window.js
index f496282..ad982b2 100644
--- a/third_party/blink/web_tests/external/wpt/screen_enumeration/getScreens.tentative.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/screen_enumeration/getScreens.tentative.https.window.js
@@ -1,39 +1,63 @@
 // META: global=window
 // META: script=/resources/testdriver.js
 // META: script=/resources/testdriver-vendor.js
-"use strict";
+'use strict';
 
-promise_test(async testCase => {
-  assert_equals(typeof self.getScreens, "function");
-}, "self.getScreens is present");
+promise_test(async t => {
+  assert_equals(typeof self.getScreens, 'function');
+}, 'getScreens() is present');
 
-promise_test(async testCase => {
-  await test_driver.set_permission({name: "window-placement"}, "granted");
+promise_test(async t => {
+  await test_driver.set_permission({name: 'window-placement'}, 'granted');
   const screens = await self.getScreens();
   assert_greater_than(screens.length, 0);
 
-  assert_equals(typeof screens[0].availWidth, "number");
-  assert_equals(typeof screens[0].availHeight, "number");
-  assert_equals(typeof screens[0].width, "number");
-  assert_equals(typeof screens[0].height, "number");
-  assert_equals(typeof screens[0].colorDepth, "number");
-  assert_equals(typeof screens[0].pixelDepth, "number");
+  assert_equals(typeof screens[0].availWidth, 'number');
+  assert_equals(typeof screens[0].availHeight, 'number');
+  assert_equals(typeof screens[0].width, 'number');
+  assert_equals(typeof screens[0].height, 'number');
+  assert_equals(typeof screens[0].colorDepth, 'number');
+  assert_equals(typeof screens[0].pixelDepth, 'number');
 
-  assert_equals(typeof screens[0].availLeft, "number");
-  assert_equals(typeof screens[0].availTop, "number");
-  assert_equals(typeof screens[0].left, "number");
-  assert_equals(typeof screens[0].top, "number");
-  assert_equals(typeof screens[0].orientation, "object");
+  assert_equals(typeof screens[0].availLeft, 'number');
+  assert_equals(typeof screens[0].availTop, 'number');
+  assert_equals(typeof screens[0].left, 'number');
+  assert_equals(typeof screens[0].top, 'number');
+  assert_equals(typeof screens[0].orientation, 'object');
 
-  assert_equals(typeof screens[0].primary, "boolean");
-  assert_equals(typeof screens[0].internal, "boolean");
-  assert_equals(typeof screens[0].scaleFactor, "number");
-  assert_equals(typeof screens[0].id, "string");
-  assert_equals(typeof screens[0].touchSupport, "boolean");
-}, "self.getScreens returns at least 1 Screen with permission granted");
+  assert_equals(typeof screens[0].primary, 'boolean');
+  assert_equals(typeof screens[0].internal, 'boolean');
+  assert_equals(typeof screens[0].scaleFactor, 'number');
+  assert_equals(typeof screens[0].id, 'string');
+  assert_equals(typeof screens[0].touchSupport, 'boolean');
+}, 'getScreens() returns at least 1 Screen with permission granted');
 
-promise_test(async testCase => {
+promise_test(async t => {
+  await test_driver.set_permission({name: 'window-placement'}, 'granted');
+  assert_greater_than((await self.getScreens()).length, 0);
   await test_driver.set_permission({name: 'window-placement'}, 'denied');
-  const screens = await self.getScreens();
-  assert_equals(screens.length, 0);
-}, 'self.getScreens returns no Screen objects with permission denied');
+  await promise_rejects_dom(t, 'NotAllowedError', self.getScreens());
+}, 'getScreens() rejects the promise with permission denied');
+
+async_test(async t => {
+  await test_driver.set_permission({name: 'window-placement'}, 'granted');
+  let iframe = document.body.appendChild(document.createElement('iframe'));
+  assert_greater_than((await iframe.contentWindow.getScreens()).length, 0);
+
+  iframe.contentWindow.onunload = t.step_func(async () => {
+    // TODO(crbug.com/1106132): This should reject or resolve; not hang.
+    // assert_greater_than((await iframe.contentWindow.getScreens()).length, 0);
+
+    let iframeGetScreens = iframe.contentWindow.getScreens;
+    let constructor = iframe.contentWindow.DOMException;
+    assert_not_equals(iframeGetScreens, undefined);
+    assert_not_equals(constructor, undefined);
+
+    await t.step_wait(() => !iframe.contentWindow, "execution context invalid");
+    assert_equals(iframe.contentWindow, null);
+    await promise_rejects_dom(t, 'InvalidStateError', constructor, iframeGetScreens());
+    t.done();
+  });
+
+  document.body.removeChild(iframe);
+}, "getScreens() resolves for attached iframe; rejects for detached iframe");
diff --git a/third_party/blink/web_tests/external/wpt/screen_enumeration/getScreens.values.https.html b/third_party/blink/web_tests/external/wpt/screen_enumeration/getScreens.values.https.html
index 3b80607..881396a 100644
--- a/third_party/blink/web_tests/external/wpt/screen_enumeration/getScreens.values.https.html
+++ b/third_party/blink/web_tests/external/wpt/screen_enumeration/getScreens.values.https.html
@@ -1,19 +1,37 @@
 <!DOCTYPE html>
-<meta charset="utf-8">
+<meta charset='utf-8'>
 <title>Screen Enumeration: getScreens() tentative</title>
 <!-- TODO: update link to W3C whenever specifications are ready -->
-<link rel="help" href="https://github.com/webscreens/screen-enumeration"/>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/screenenumeration-helpers.js"></script>
+<link rel='help' href='https://github.com/webscreens/screen-enumeration'/>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='resources/screenenumeration-helpers.js'></script>
 <script>
 
-"use strict";
+'use strict';
+
+function check_screen_matches_display(screen, display) {
+  assert_equals(screen.left, display.bounds.x);
+  assert_equals(screen.top, display.bounds.y);
+  assert_equals(screen.width, display.bounds.width);
+  assert_equals(screen.height, display.bounds.height);
+  assert_equals(screen.availLeft, display.workArea.x);
+  assert_equals(screen.availTop, display.workArea.y);
+  assert_equals(screen.availWidth, display.workArea.width);
+  assert_equals(screen.availHeight, display.workArea.height);
+  assert_equals(screen.scaleFactor, display.deviceScaleFactor);
+}
+
+screen_enumeration_test(async (t, mockScreenEnum) => {
+  mockScreenEnum.setSuccess(true);
+  await test_driver.set_permission({name: 'window-placement'}, 'granted');
+  assert_equals((await self.getScreens()).length, 0);
+}, 'getScreens() supports an empty set of mocked screens');
 
 screen_enumeration_test(async (t, mockScreenEnum) => {
   let display1 = makeDisplay(10,
-                             {x: 0, y: 10, width: 1200, height: 800},
-                             {x: 20, y: 30, width: 1000, height: 600},
+                             {x: 0, y: 0, width: 800, height: 600},
+                             {x: 0, y: 0, width: 800, height: 550},
                              1.0);
 
   mockScreenEnum.addDisplay(display1);
@@ -21,43 +39,31 @@
   mockScreenEnum.setInternalId(mockScreenEnum.displays_[0].id);
   mockScreenEnum.setSuccess(true);
 
-  // Grant window-placement permissions for testdriver.
-  await test_driver.set_permission({name: "window-placement"}, "granted");
+  await test_driver.set_permission({name: 'window-placement'}, 'granted');
 
-  // This returns the mocked displays via MockScreenEnumeration implementation.
   const screens = await self.getScreens();
-
   assert_equals(screens.length, 1);
-
-  assert_equals(screens[0].left, 0);
-  assert_equals(screens[0].top, 10);
-  assert_equals(screens[0].width, 1200);
-  assert_equals(screens[0].height, 800);
-  assert_equals(screens[0].availLeft, 20);
-  assert_equals(screens[0].availTop, 30);
-  assert_equals(screens[0].availWidth, 1000);
-  assert_equals(screens[0].availHeight, 600);
+  check_screen_matches_display(screens[0], display1);
   assert_equals(screens[0].primary, true);
   assert_equals(screens[0].internal, true);
-  assert_equals(screens[0].scaleFactor, 1.0);
-  assert_equals(screens[0].id, "0");
-}, "getScreens() returns a single mocked screen");
+  assert_equals(screens[0].id, '0');
+}, 'getScreens() supports a single mocked screen');
 
 screen_enumeration_test(async (t, mockScreenEnum) => {
   let display1 = makeDisplay(10,
-                             {x: 0, y: 10, width: 1200, height: 800},
-                             {x: 20, y: 30, width: 1000, height: 600},
+                             {x: 0, y: 0, width: 800, height: 600},
+                             {x: 0, y: 0, width: 800, height: 550},
                              1.0);
 
   let display2 = makeDisplay(11,
-                             {x: 0, y: 10, width: 1200, height: 800},
-                             {x: 20, y: 30, width: 1000, height: 600},
-                             1.0);
+                             {x: 800, y: 0, width: 800, height: 600},
+                             {x: 800, y: 0, width: 800, height: 550},
+                             2.0);
 
   let display3 = makeDisplay(12,
-                             {x: 0, y: 10, width: 1200, height: 800},
-                             {x: 20, y: 30, width: 1000, height: 600},
-                             1.0);
+                             {x: 0, y: 600, width: 1200, height: 800},
+                             {x: 50, y: 50, width: 1150, height: 750},
+                             1.5);
 
   mockScreenEnum.addDisplay(display1);
   mockScreenEnum.addDisplay(display2);
@@ -66,29 +72,42 @@
   mockScreenEnum.setInternalId(mockScreenEnum.displays_[0].id);
   mockScreenEnum.setSuccess(true);
 
-  // Grant window-placement permissions for testdriver.
-  await test_driver.set_permission({name: "window-placement"}, "granted");
+  await test_driver.set_permission({name: 'window-placement'}, 'granted');
 
-  // This returns the mocked displays via MockScreenEnumeration implementation.
   let screens = await self.getScreens();
-
   assert_equals(screens.length, 3);
-  assert_equals(screens[0].id, "0");
-  assert_equals(screens[1].id, "1");
-  assert_equals(screens[2].id, "2");
+  check_screen_matches_display(screens[0], display1);
+  assert_equals(screens[0].primary, true);
+  assert_equals(screens[0].internal, true);
+  assert_equals(screens[0].id, '0');
+  check_screen_matches_display(screens[1], display2);
+  assert_equals(screens[1].primary, false);
+  assert_equals(screens[1].internal, false);
+  assert_equals(screens[1].id, '1');
+  check_screen_matches_display(screens[2], display3);
+  assert_equals(screens[2].primary, false);
+  assert_equals(screens[2].internal, false);
+  assert_equals(screens[2].id, '2');
 
   mockScreenEnum.removeDisplay(display2.id);
-
   screens = await self.getScreens();
   assert_equals(screens.length, 2);
-  assert_equals(screens[0].id, "0");
-  assert_equals(screens[1].id, "1");
+  check_screen_matches_display(screens[0], display1);
+  assert_equals(screens[0].id, '0');
+  check_screen_matches_display(screens[1], display3);
+  assert_equals(screens[1].id, '1');
 
   mockScreenEnum.removeDisplay(display1.id);
-
   screens = await self.getScreens();
   assert_equals(screens.length, 1);
-  assert_equals(screens[0].id, "0");
-}, "getScreens() supports multiple mocked screens");
+  check_screen_matches_display(screens[0], display3);
+  assert_equals(screens[0].id, '0');
+}, 'getScreens() supports multiple mocked screens');
+
+screen_enumeration_test(async (t, mockScreenEnum) => {
+  mockScreenEnum.setSuccess(false);
+  await test_driver.set_permission({name: 'window-placement'}, 'granted');
+  promise_rejects_dom(t, 'NotAllowedError', self.getScreens());
+}, 'getScreens() rejects when the mock success value is set to false');
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/screen_enumeration/isMultiScreen.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/screen_enumeration/isMultiScreen.tentative.https.window.js
new file mode 100644
index 0000000..515731c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/screen_enumeration/isMultiScreen.tentative.https.window.js
@@ -0,0 +1,40 @@
+// META: global=window
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+'use strict';
+
+promise_test(async t => {
+  assert_equals(typeof self.isMultiScreen, 'function');
+}, 'isMultiScreen() is present');
+
+promise_test(async t => {
+  await test_driver.set_permission({name: 'window-placement'}, 'granted');
+  assert_equals(typeof await self.isMultiScreen(), 'boolean');
+}, 'isMultiScreen() returns a boolean value with permission granted');
+
+promise_test(async t => {
+  await test_driver.set_permission({name: 'window-placement'}, 'denied');
+  assert_equals(typeof await self.isMultiScreen(), 'boolean');
+}, 'isMultiScreen() returns a boolean value with permission denied');
+
+async_test(async t => {
+  let iframe = document.body.appendChild(document.createElement('iframe'));
+  assert_equals(typeof await iframe.contentWindow.isMultiScreen(), 'boolean');
+
+  iframe.contentWindow.onunload = t.step_func(async () => {
+    // TODO(crbug.com/1106132): This should reject or resolve; not hang.
+    // assert_equals(typeof await iframe.contentWindow.isMultiScreen(), 'boolean');
+
+    let iframeIsMultiScreen = iframe.contentWindow.isMultiScreen;
+    let constructor = iframe.contentWindow.DOMException;
+    assert_not_equals(iframeIsMultiScreen, undefined);
+    assert_not_equals(constructor, undefined);
+
+    await t.step_wait(() => !iframe.contentWindow, "execution context invalid");
+    assert_equals(iframe.contentWindow, null);
+    await promise_rejects_dom(t, 'InvalidStateError', constructor, iframeIsMultiScreen());
+    t.done();
+  });
+
+  document.body.removeChild(iframe);
+}, "isMultiScreen() resolves for attached iframe; rejects for detached iframe");
diff --git a/third_party/blink/web_tests/external/wpt/screen_enumeration/isMultiScreen.values.https.html b/third_party/blink/web_tests/external/wpt/screen_enumeration/isMultiScreen.values.https.html
new file mode 100644
index 0000000..5146803
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/screen_enumeration/isMultiScreen.values.https.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>Screen Enumeration: isMultiScreen() tentative</title>
+<!-- TODO: update link to W3C whenever specifications are ready -->
+<link rel='help' href='https://github.com/webscreens/screen-enumeration'/>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='resources/screenenumeration-helpers.js'></script>
+<script>
+
+'use strict';
+
+screen_enumeration_test(async (t, mockScreenEnum) => {
+  let display1 = makeDisplay(10,
+                             {x: 0, y: 0, width: 800, height: 600},
+                             {x: 0, y: 0, width: 800, height: 550},
+                             1.0);
+
+  let display2 = makeDisplay(11,
+                             {x: 800, y: 0, width: 800, height: 600},
+                             {x: 800, y: 0, width: 800, height: 550},
+                             2.0);
+
+  mockScreenEnum.setSuccess(true);
+  assert_equals(await self.isMultiScreen(), false);
+
+  mockScreenEnum.addDisplay(display1);
+  assert_equals(await self.isMultiScreen(), false);
+
+  mockScreenEnum.addDisplay(display2);
+  assert_equals(await self.isMultiScreen(), true);
+
+  mockScreenEnum.removeDisplay(display2.id);
+  assert_equals(await self.isMultiScreen(), false);
+}, 'isMultiScreen() works as expected with mocked screens');
+
+screen_enumeration_test(async (t, mockScreenEnum) => {
+  mockScreenEnum.setSuccess(false);
+  promise_rejects_dom(t, 'NotAllowedError', self.isMultiScreen());
+}, 'isMultiScreen() rejects when the mock success value is set to false');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/epochs_update.sh b/third_party/blink/web_tests/external/wpt/tools/ci/epochs_update.sh
index 4b393fa..f460814 100755
--- a/third_party/blink/web_tests/external/wpt/tools/ci/epochs_update.sh
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/epochs_update.sh
@@ -26,7 +26,12 @@
     do
         EPOCH=$(get_epoch_timeval ${e})
         EPOCH_BRANCH_NAME=$(get_epoch_branch_name ${e})
-        git branch ${EPOCH_BRANCH_NAME} $(./wpt rev-list --epoch ${EPOCH})
+        EPOCH_SHA=$(./wpt rev-list --epoch ${EPOCH})
+        if [ "${EPOCH_SHA}" = "" ]; then
+            echo "ERROR: Empty SHA returned from ./wpt rev-list"
+            exit 1
+        fi
+        git branch "${EPOCH_BRANCH_NAME}" "${EPOCH_SHA}"
         ALL_BRANCHES_NAMES="${ALL_BRANCHES_NAMES} ${EPOCH_BRANCH_NAME}"
     done
     # This is safe because `git push` will by default fail for a non-fast-forward
diff --git a/third_party/blink/web_tests/external/wpt/tools/lint/lint.py b/third_party/blink/web_tests/external/wpt/tools/lint/lint.py
index 14052e7..ad053dcc 100644
--- a/third_party/blink/web_tests/external/wpt/tools/lint/lint.py
+++ b/third_party/blink/web_tests/external/wpt/tools/lint/lint.py
@@ -48,6 +48,11 @@
     # ignores the error.
     Ignorelist = Dict[Text, Dict[Text, Set[Optional[int]]]]
 
+    try:
+        from xml.etree import cElementTree as ElementTree
+    except ImportError:
+        from xml.etree import ElementTree as ElementTree  # type: ignore
+
 
 logger = None  # type: Optional[logging.Logger]
 
@@ -521,6 +526,9 @@
         if timeout_value != "long":
             errors.append(rules.InvalidTimeout.error(path, (timeout_value,)))
 
+    required_elements = []  # type: List[Text]
+
+    testharnessreport_nodes = []  # type: List[ElementTree.Element]
     if source_file.testharness_nodes:
         test_type = source_file.manifest_items()[0]
         if test_type not in ("testharness", "manual"):
@@ -543,31 +551,12 @@
                 if variant != "" and variant[0] not in ("?", "#"):
                     errors.append(rules.MalformedVariant.error(path, (path,)))
 
-        seen_elements = {"timeout": False,
-                         "testharness": False,
-                         "testharnessreport": False}
-        required_elements = [key for key, value in {"testharness": True,
-                                                    "testharnessreport": len(testharnessreport_nodes) > 0,
-                                                    "timeout": len(source_file.timeout_nodes) > 0}.items()
-                             if value]
+        required_elements.extend(key for key, value in {"testharness": True,
+                                                        "testharnessreport": len(testharnessreport_nodes) > 0,
+                                                        "timeout": len(source_file.timeout_nodes) > 0}.items()
+                                 if value)
 
-        for elem in source_file.root.iter():
-            if source_file.timeout_nodes and elem == source_file.timeout_nodes[0]:
-                seen_elements["timeout"] = True
-                if seen_elements["testharness"]:
-                    errors.append(rules.LateTimeout.error(path))
-
-            elif elem == source_file.testharness_nodes[0]:
-                seen_elements["testharness"] = True
-
-            elif testharnessreport_nodes and elem == testharnessreport_nodes[0]:
-                seen_elements["testharnessreport"] = True
-                if not seen_elements["testharness"]:
-                    errors.append(rules.EarlyTestharnessReport.error(path))
-
-            if all(seen_elements[name] for name in required_elements):
-                break
-
+    testdriver_vendor_nodes = []  # type: List[ElementTree.Element]
     if source_file.testdriver_nodes:
         if len(source_file.testdriver_nodes) > 1:
             errors.append(rules.MultipleTestdriver.error(path))
@@ -579,6 +568,38 @@
             if len(testdriver_vendor_nodes) > 1:
                 errors.append(rules.MultipleTestdriverVendor.error(path))
 
+        required_elements.append("testdriver")
+        if len(testdriver_vendor_nodes) > 0:
+            required_elements.append("testdriver-vendor")
+
+    if required_elements:
+        seen_elements = defaultdict(bool)
+
+        for elem in source_file.root.iter():
+            if source_file.timeout_nodes and elem == source_file.timeout_nodes[0]:
+                seen_elements["timeout"] = True
+                if seen_elements["testharness"]:
+                    errors.append(rules.LateTimeout.error(path))
+
+            elif source_file.testharness_nodes and elem == source_file.testharness_nodes[0]:
+                seen_elements["testharness"] = True
+
+            elif testharnessreport_nodes and elem == testharnessreport_nodes[0]:
+                seen_elements["testharnessreport"] = True
+                if not seen_elements["testharness"]:
+                    errors.append(rules.EarlyTestharnessReport.error(path))
+
+            elif source_file.testdriver_nodes and elem == source_file.testdriver_nodes[0]:
+                seen_elements["testdriver"] = True
+
+            elif testdriver_vendor_nodes and elem == testdriver_vendor_nodes[0]:
+                seen_elements["testdriver-vendor"] = True
+                if not seen_elements["testdriver"]:
+                    errors.append(rules.EarlyTestdriverVendor.error(path))
+
+            if all(seen_elements[name] for name in required_elements):
+                break
+
     for element in source_file.root.findall(".//{http://www.w3.org/1999/xhtml}script[@src]"):
         src = element.attrib["src"]
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/lint/rules.py b/third_party/blink/web_tests/external/wpt/tools/lint/rules.py
index 6ffd749..c6774579 100644
--- a/third_party/blink/web_tests/external/wpt/tools/lint/rules.py
+++ b/third_party/blink/web_tests/external/wpt/tools/lint/rules.py
@@ -242,6 +242,16 @@
     to_fix = "flip the order"
 
 
+class EarlyTestdriverVendor(Rule):
+    name = "EARLY-TESTDRIVER-VENDOR"
+    description = collapse("""
+        Test file has an instance of
+        `<script src='/resources/testdriver-vendor.js'>` prior to
+        `<script src='/resources/testdriver.js'>`
+    """)
+    to_fix = "flip the order"
+
+
 class MultipleTestdriver(Rule):
     name = "MULTIPLE-TESTDRIVER"
     description = "More than one `<script src='/resources/testdriver.js'>`"
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptserve/wptserve/server.py b/third_party/blink/web_tests/external/wpt/tools/wptserve/wptserve/server.py
index c2498c4e..56342ed2 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptserve/wptserve/server.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptserve/wptserve/server.py
@@ -29,6 +29,16 @@
 from .utils import HTTPException
 from .constants import h2_headers
 
+# We need to stress test that browsers can send/receive many headers (there is
+# no specified limit), but the Python stdlib has an arbitrary limit of 100
+# headers. Hitting the limit would produce an exception that is silently caught
+# in Python 2 but leads to HTTP 431 in Python 3, so we monkey patch it higher.
+# https://bugs.python.org/issue26586
+# https://github.com/web-platform-tests/wpt/pull/24451
+from six.moves import http_client
+assert isinstance(getattr(http_client, '_MAXHEADERS'), int)
+setattr(http_client, '_MAXHEADERS', 512)
+
 """
 HTTP server designed for testing purposes.
 
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/constructor-allowed-to-start-expected.txt b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/constructor-allowed-to-start-expected.txt
new file mode 100644
index 0000000..f7801ac
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/constructor-allowed-to-start-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL AudioContext state around "allowed to start" in constructor Unhandled rejection: assert_equals: initial state expected "suspended" but got "running"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/constructor-allowed-to-start.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/constructor-allowed-to-start.html
new file mode 100644
index 0000000..f866b5f7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/constructor-allowed-to-start.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>AudioContext state around "allowed to start" in constructor</title>
+<link rel=help href=https://webaudio.github.io/web-audio-api/#dom-audiocontext-audiocontext>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script>
+setup({ single_test: true });
+test_driver.bless("audio playback", () => {
+  const ctx = new AudioContext();
+  // Immediately after the constructor the state is "suspended" because the
+  // control message to start processing has just been sent, but the state
+  // should change soon.
+  assert_equals(ctx.state, "suspended", "initial state");
+  ctx.onstatechange = () => {
+    assert_equals(ctx.state, "running", "state after statechange event");
+    // Now create another context and ensure it starts out in the "suspended"
+    // state too, ensuring it's not synchronously "running".
+    const ctx2 = new AudioContext();
+    assert_equals(ctx2.state, "suspended", "initial state of 2nd context");
+    done();
+  };
+});
+</script>
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/animate-fill-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/animate-fill-expected.txt
index e9728f4c..d8b2f9a 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/animate-fill-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/animate-fill-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 100, 100]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-change-target-id-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-change-target-id-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-change-target-id-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-change-target-id-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
index 312b0a7d..edbaad6 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
@@ -6,8 +6,8 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [45, 0, 60, 110],
-        [0, 0, 55, 110]
+        [45, 0, 60, 90],
+        [0, 0, 55, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-reference-invalidation-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-remove-target-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-remove-target-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-remove-target-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-remove-target-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-add-to-document-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-changes-id-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-changes-id-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-changes-id-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-changes-id-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-id-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-id-change-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-id-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-id-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-property-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-property-change-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-property-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-property-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-style-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-style-change-expected.txt
index e9728f4c..ef51adec 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-style-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/feImage-target-style-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-child-repaint-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-child-repaint-expected.txt
index 2468eee..20ae27a 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-child-repaint-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-child-repaint-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 106, 106]
+        [0, 0, 105, 105]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-reference-change-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-reference-change-expected.txt
index ec0912f..ae2e746 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-reference-change-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-reference-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [8, 8, 110, 110]
+        [8, 8, 100, 100]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-refresh-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-refresh-expected.txt
index 7bdd985..848593f 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-refresh-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/filter-refresh-expected.txt
@@ -6,17 +6,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [10, 170, 132, 36],
-        [10, 130, 132, 36],
-        [140, 140, 36, 36],
-        [140, 100, 36, 36],
-        [140, 60, 36, 36],
+        [10, 170, 116, 36],
+        [10, 130, 116, 36],
         [90, 90, 36, 36],
         [90, 50, 36, 36],
         [50, 90, 36, 36],
         [50, 50, 36, 36],
-        [10, 90, 36, 36],
-        [10, 50, 36, 36]
+        [140, 140, 30, 30],
+        [140, 100, 30, 30],
+        [140, 60, 30, 30],
+        [10, 90, 30, 30],
+        [10, 50, 30, 30]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/resource-invalidate-on-target-update-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/resource-invalidate-on-target-update-expected.txt
index dd45b79..5badbc6b 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/resource-invalidate-on-target-update-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/resource-invalidate-on-target-update-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 240, 230, 120],
+        [0, 240, 222, 120],
         [10, 130, 200, 100],
         [10, 10, 200, 100]
       ]
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/use-disappears-after-style-update-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/use-disappears-after-style-update-expected.txt
index a1e32eb..06f13ea 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/use-disappears-after-style-update-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/use-disappears-after-style-update-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [50, 10, 36, 36],
+        [50, 10, 30, 30],
         [10, 10, 30, 30]
       ]
     }
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/animate-fill-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/animate-fill-expected.txt
index 637c4ce7..86c58e2 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/animate-fill-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/animate-fill-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 100, 100]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-change-target-id-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-change-target-id-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-change-target-id-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-change-target-id-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
index 443d522..0e209ad 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-multiple-targets-id-change-expected.txt
@@ -6,8 +6,8 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [45, 0, 60, 110],
-        [0, 0, 55, 110]
+        [45, 0, 60, 90],
+        [0, 0, 55, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-reference-invalidation-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-reference-invalidation-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-remove-target-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-remove-target-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-remove-target-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-remove-target-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-add-to-document-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-add-to-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-2-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-attribute-change-with-use-indirection-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-changes-id-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-changes-id-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-changes-id-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-changes-id-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-id-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-id-change-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-id-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-id-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-inline-style-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-property-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-property-change-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-property-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-property-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-reappend-to-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-remove-from-document-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-style-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-style-change-expected.txt
index 637c4ce7..efc0f56 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-style-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/feImage-target-style-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 110, 110]
+        [0, 0, 90, 90]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/filter-child-repaint-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/filter-child-repaint-expected.txt
index 5d22e0c..4b7fd1b7 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/filter-child-repaint-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/filter-child-repaint-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 0, 106, 106]
+        [0, 0, 105, 105]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/filter-reference-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/filter-reference-change-expected.txt
index 9c3b9834..e6987437 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/filter-reference-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/filter-reference-change-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [8, 8, 110, 110]
+        [8, 8, 100, 100]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/filter-refresh-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/filter-refresh-expected.txt
index 07f2db2..28e9a09 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/filter-refresh-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/filter-refresh-expected.txt
@@ -6,17 +6,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [10, 170, 132, 36],
-        [10, 130, 132, 36],
-        [140, 140, 36, 36],
-        [140, 100, 36, 36],
-        [140, 60, 36, 36],
+        [10, 170, 116, 36],
+        [10, 130, 116, 36],
         [90, 90, 36, 36],
         [90, 50, 36, 36],
         [50, 90, 36, 36],
         [50, 50, 36, 36],
-        [10, 90, 36, 36],
-        [10, 50, 36, 36]
+        [140, 140, 30, 30],
+        [140, 100, 30, 30],
+        [140, 60, 30, 30],
+        [10, 90, 30, 30],
+        [10, 50, 30, 30]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/resource-invalidate-on-target-update-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/resource-invalidate-on-target-update-expected.txt
index 0ef177a3..14b080f 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/resource-invalidate-on-target-update-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/resource-invalidate-on-target-update-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [0, 240, 230, 120],
+        [0, 240, 222, 120],
         [10, 130, 200, 100],
         [10, 10, 200, 100]
       ]
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/use-disappears-after-style-update-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/use-disappears-after-style-update-expected.txt
index e9b714b3..fab71d4 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/use-disappears-after-style-update-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/use-disappears-after-style-update-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [50, 10, 36, 36],
+        [50, 10, 30, 30],
         [10, 10, 30, 30]
       ]
     }
diff --git a/third_party/blink/web_tests/platform/mac/svg/batik/text/textProperties-expected.png b/third_party/blink/web_tests/platform/mac/svg/batik/text/textProperties-expected.png
index d883cd4..56eed6fd 100644
--- a/third_party/blink/web_tests/platform/mac/svg/batik/text/textProperties-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/batik/text/textProperties-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index baacc4d..855f636 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -11932,6 +11932,7 @@
     method getScreens
     method getSelection
     method getWindowSegments
+    method isMultiScreen
     method matchMedia
     method moveBy
     method moveTo
diff --git a/third_party/feed_library/OWNERS b/third_party/feed_library/OWNERS
index 0cce6ad..7f83c34 100644
--- a/third_party/feed_library/OWNERS
+++ b/third_party/feed_library/OWNERS
@@ -1,7 +1,4 @@
-carlosk@chromium.org
-fgorski@chromium.org
-harringtond@chromium.org
-skym@chromium.org
+file://components/feed/OWNERS
 
 # Team: feed@chromium.org
 # COMPONENT: UI>Browser>ContentSuggestions>Feed
diff --git a/third_party/tint/README.chromium b/third_party/tint/README.chromium
index 82ec055d..fb8ea9e 100644
--- a/third_party/tint/README.chromium
+++ b/third_party/tint/README.chromium
@@ -2,7 +2,7 @@
 Short Name: tint
 URL: https://dawn.googlesource.com/tint
 Version: 0
-Revision: 919011af0a2ae1b663aae0aaa4083a3f9f13e66d
+Revision: 5f43fedcddba8fd4cc3d9ff21b2ae8c5f8f4f4bc
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/tools/android/dependency_analysis/js/src/graph_view.js b/tools/android/dependency_analysis/js/src/graph_view.js
index c314ce8..58e5d19 100644
--- a/tools/android/dependency_analysis/js/src/graph_view.js
+++ b/tools/android/dependency_analysis/js/src/graph_view.js
@@ -158,16 +158,28 @@
     const width = +svg.attr('width');
     const height = +svg.attr('height');
 
-    const centeringStrengthY = 1.0;
+    const centeringStrengthY = 0.1;
     const centeringStrengthX = centeringStrengthY * (height / width);
     /** @private {*} */
     this.simulation_ = d3.forceSimulation()
         .alphaMin(SIMULATION_SPEED_PARAMS.ALPHA_MIN)
         .force('chargeForce', d3.forceManyBody().strength(-3000))
         .force('centerXForce',
-            d3.forceX(width / 2).strength(centeringStrengthX))
+            d3.forceX(width / 2).strength(node => {
+              if (node.visualizationState.selectedByFilter)
+                return centeringStrengthX * 20;
+              if (node.visualizationState.outboundDepth <= 1)
+                return centeringStrengthX * 5;
+              return centeringStrengthY;
+            }))
         .force('centerYForce',
-            d3.forceY(height / 2).strength(centeringStrengthY));
+            d3.forceY(height / 2).strength(node => {
+              if (node.visualizationState.selectedByFilter)
+                return centeringStrengthY * 20;
+              if (node.visualizationState.outboundDepth <= 1)
+                return centeringStrengthY * 5;
+              return centeringStrengthY;
+            }));
 
     /** @private {number} */
     this.reheatTicks_ = countNumReheatTicks();
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 31a21f28..b49e0f5 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -16748,6 +16748,109 @@
   <int value="7" label="UnsizedMedia"/>
 </enum>
 
+<enum name="DocumentScanSaneBackend">
+  <summary>
+    Various SANE backends (effectively scanner drivers) that can be used to scan
+    a document in Chrome OS. These values are defined in the SaneBackend enum in
+    chromeos/src/platform2/lorgnette/enums.h.
+  </summary>
+  <int value="0" label="UnknownBackend"/>
+  <int value="1" label="Abaton"/>
+  <int value="2" label="Agfafocus"/>
+  <int value="3" label="AirscanBrother"/>
+  <int value="4" label="AirscanCanon"/>
+  <int value="5" label="AirscanEpson"/>
+  <int value="6" label="AirscanHp"/>
+  <int value="7" label="AirscanKodak"/>
+  <int value="8" label="AirscanKonicaMinolta"/>
+  <int value="9" label="AirscanKyocera"/>
+  <int value="10" label="AirscanLexmark"/>
+  <int value="11" label="AirscanOther"/>
+  <int value="12" label="AirscanRicoh"/>
+  <int value="13" label="AirscanSamsung"/>
+  <int value="14" label="AirscanXerox"/>
+  <int value="15" label="Apple"/>
+  <int value="16" label="Artec"/>
+  <int value="17" label="ArtecEplus48U"/>
+  <int value="18" label="As6E"/>
+  <int value="19" label="Avision"/>
+  <int value="20" label="Bh"/>
+  <int value="21" label="Canon"/>
+  <int value="22" label="Canon630U"/>
+  <int value="23" label="CanonDr"/>
+  <int value="24" label="Cardscan"/>
+  <int value="25" label="Coolscan"/>
+  <int value="26" label="Coolscan2"/>
+  <int value="27" label="Coolscan3"/>
+  <int value="28" label="Dc210"/>
+  <int value="29" label="Dc240"/>
+  <int value="30" label="Dc25"/>
+  <int value="31" label="Dell1600NNet"/>
+  <int value="32" label="Dmc"/>
+  <int value="33" label="Epjitsu"/>
+  <int value="34" label="Epson"/>
+  <int value="35" label="Epson2"/>
+  <int value="36" label="Escl"/>
+  <int value="37" label="Fujitsu"/>
+  <int value="38" label="Genesys"/>
+  <int value="39" label="Gt68Xx"/>
+  <int value="40" label="Hp"/>
+  <int value="41" label="Hp3500"/>
+  <int value="42" label="Hp3900"/>
+  <int value="43" label="Hp4200"/>
+  <int value="44" label="Hp5400"/>
+  <int value="45" label="Hp5590"/>
+  <int value="46" label="Hpljm1005"/>
+  <int value="47" label="Hs2P"/>
+  <int value="48" label="Ibm"/>
+  <int value="49" label="Kodak"/>
+  <int value="50" label="Kodakaio"/>
+  <int value="51" label="Kvs1025"/>
+  <int value="52" label="Kvs20Xx"/>
+  <int value="53" label="Kvs40Xx"/>
+  <int value="54" label="Leo"/>
+  <int value="55" label="Lexmark"/>
+  <int value="56" label="Ma1509"/>
+  <int value="57" label="Magicolor"/>
+  <int value="58" label="Matsushita"/>
+  <int value="59" label="Microtek"/>
+  <int value="60" label="Microtek2"/>
+  <int value="61" label="Mustek"/>
+  <int value="62" label="MustekUsb"/>
+  <int value="63" label="MustekUsb2"/>
+  <int value="64" label="Nec"/>
+  <int value="65" label="Net"/>
+  <int value="66" label="Niash"/>
+  <int value="67" label="P5"/>
+  <int value="68" label="Pie"/>
+  <int value="69" label="Pixma"/>
+  <int value="70" label="Plustek"/>
+  <int value="71" label="PlustekPp"/>
+  <int value="72" label="Qcam"/>
+  <int value="73" label="Ricoh"/>
+  <int value="74" label="Ricoh2"/>
+  <int value="75" label="Rts8891"/>
+  <int value="76" label="S9036"/>
+  <int value="77" label="Sceptre"/>
+  <int value="78" label="Sharp"/>
+  <int value="79" label="Sm3600"/>
+  <int value="80" label="Sm3840"/>
+  <int value="81" label="Snapscan"/>
+  <int value="82" label="Sp15C"/>
+  <int value="83" label="St400"/>
+  <int value="84" label="Stv680"/>
+  <int value="85" label="Tamarack"/>
+  <int value="86" label="Teco1"/>
+  <int value="87" label="Teco2"/>
+  <int value="88" label="Teco3"/>
+  <int value="89" label="Test"/>
+  <int value="90" label="U12"/>
+  <int value="91" label="Umax"/>
+  <int value="92" label="Umax1220U"/>
+  <int value="93" label="UmaxPp"/>
+  <int value="94" label="XeroxMfp"/>
+</enum>
+
 <enum name="DocumentStateForDeferredLoading">
   <int value="0" label="Created"/>
   <int value="1" label="WouldLoadBecauseVisible"/>
@@ -18147,6 +18250,12 @@
   <int value="2" label="Schedule removed"/>
 </enum>
 
+<enum name="DownloadLaterPromptStatus">
+  <int value="0" label="Show initial"/>
+  <int value="1" label="Show prompt"/>
+  <int value="2" label="Don't show prompt"/>
+</enum>
+
 <enum name="DownloadLaterUiEvent">
   <int value="0" label="DOWNLOAD_LATER_DIALOG_SHOW"/>
   <int value="1" label="DOWNLOAD_LATER_DIALOG_COMPLETE"/>
@@ -40064,6 +40173,7 @@
   <int value="-1573468162" label="ClientStorageAccessContextAuditing:enabled"/>
   <int value="-1572010356" label="enable-privet-v3"/>
   <int value="-1571841513" label="enable-devtools-experiments"/>
+  <int value="-1568737447" label="InsecureFormSubmissionInterstitial:enabled"/>
   <int value="-1568559155" label="WebSocketHandshakeReuseConnection:enabled"/>
   <int value="-1567727398" label="DesktopPWAsWithoutExtensions:disabled"/>
   <int value="-1564232131"
@@ -41975,6 +42085,7 @@
   <int value="492985975" label="use-monitor-color-space"/>
   <int value="493903641" label="disable-appcontainer"/>
   <int value="494733611" label="disable-drop-sync-credential"/>
+  <int value="494939785" label="InsecureFormSubmissionInterstitial:disabled"/>
   <int value="497137719" label="OmniboxVoiceSearchAlwaysVisible:disabled"/>
   <int value="500177932" label="ArcSmartTextSelection:disabled"/>
   <int value="501477022" label="DrawOcclusion:enabled"/>
@@ -42318,6 +42429,7 @@
   <int value="871435095" label="EnableAggregatedMlAppRanking:disabled"/>
   <int value="871713352" label="ImprovedLanguageSettings:enabled"/>
   <int value="876879670" label="OfflinePagesInDownloadHomeOpenInCct:enabled"/>
+  <int value="877059804" label="AutofillPreventMixedFormsFilling:disabled"/>
   <int value="878773995" label="ChromeHomeBottomNavLabels:disabled"/>
   <int value="879699575" label="disable-gesture-tap-highlight"/>
   <int value="879992337" label="disable-pull-to-refresh-effect"/>
@@ -43115,6 +43227,7 @@
   <int value="1747279677" label="disable-delegated-renderer"/>
   <int value="1748481830" label="AppManagement:enabled"/>
   <int value="1749028785" label="SubresourceRedirect:disabled"/>
+  <int value="1750693293" label="AutofillPreventMixedFormsFilling:enabled"/>
   <int value="1752168018" label="enable-stale-while-revalidate"/>
   <int value="1755024316" label="HostWindowsInAppShimProcess:disabled"/>
   <int value="1758262950"
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index a10e116..bb1e7155 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -43364,8 +43364,33 @@
   </summary>
 </histogram>
 
+<histogram name="DocumentScan.ScanFailed" enum="DocumentScanSaneBackend"
+    expires_after="2021-01-14">
+  <owner>fletcherw@chromium.org</owner>
+  <owner>bmgordon@chromium.org</owner>
+  <owner>project-bolton@google.com</owner>
+  <summary>
+    Chrome OS document scan metric that tracks which SANE backend (effectively a
+    scanner driver) was used when a scan failed.
+  </summary>
+</histogram>
+
+<histogram name="DocumentScan.ScanRequested" enum="DocumentScanSaneBackend"
+    expires_after="2021-01-14">
+  <owner>fletcherw@chromium.org</owner>
+  <owner>bmgordon@chromium.org</owner>
+  <owner>project-bolton@google.com</owner>
+  <summary>
+    Chrome OS document scan metric that tracks which SANE backend (effectively a
+    scanner driver) that a user requested to use for a scan.
+  </summary>
+</histogram>
+
 <histogram name="DocumentScan.ScanResult" enum="BooleanSuccess"
     expires_after="M90">
+  <obsolete>
+    Removed July 2020.
+  </obsolete>
   <owner>fletcherw@chromium.org</owner>
   <owner>bmgordon@chromium.org</owner>
   <summary>
@@ -43374,6 +43399,17 @@
   </summary>
 </histogram>
 
+<histogram name="DocumentScan.ScanSucceeded" enum="DocumentScanSaneBackend"
+    expires_after="2021-01-14">
+  <owner>fletcherw@chromium.org</owner>
+  <owner>bmgordon@chromium.org</owner>
+  <owner>project-bolton@google.com</owner>
+  <summary>
+    Chrome OS document scan metric that tracks which SANE backend (effectively a
+    scanner driver) was used when a scan succeeded.
+  </summary>
+</histogram>
+
 <histogram name="DomainBoundCerts.DBExists" enum="BooleanExists"
     expires_after="M77">
   <obsolete>
@@ -55953,6 +55989,78 @@
   </summary>
 </histogram>
 
+<histogram name="Extensions.DeclarativeNetRequest.RequestHeaderAdded"
+    enum="WebRequest.RequestHeader" expires_after="2021-07-01">
+  <owner>karandeepb@chromium.org</owner>
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>kelvinjiang@chromium.org</owner>
+  <summary>
+    Records the request header added by extensions using the
+    DeclarativeNetRequest API. Recorded for each network request during the
+    OnBeforeSendHeaders stage. Multiple samples can be recorded per request.
+  </summary>
+</histogram>
+
+<histogram name="Extensions.DeclarativeNetRequest.RequestHeaderChanged"
+    enum="WebRequest.RequestHeader" expires_after="2021-07-01">
+  <owner>karandeepb@chromium.org</owner>
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>kelvinjiang@chromium.org</owner>
+  <summary>
+    Records the request header modified by extensions using the
+    DeclarativeNetRequest API. Recorded for each network request during the
+    OnBeforeSendHeaders stage. Multiple samples can be recorded per request.
+  </summary>
+</histogram>
+
+<histogram name="Extensions.DeclarativeNetRequest.RequestHeaderRemoved"
+    enum="WebRequest.RequestHeader" expires_after="2021-07-01">
+  <owner>karandeepb@chromium.org</owner>
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>kelvinjiang@chromium.org</owner>
+  <summary>
+    Records the request header removed by extensions using the
+    DeclarativeNetRequest API. Recorded for each network request during the
+    OnBeforeSendHeaders stage. Multiple samples can be recorded per request.
+  </summary>
+</histogram>
+
+<histogram name="Extensions.DeclarativeNetRequest.ResponseHeaderAdded"
+    enum="WebRequest.ResponseHeader" expires_after="2021-07-01">
+  <owner>karandeepb@chromium.org</owner>
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>kelvinjiang@chromium.org</owner>
+  <summary>
+    Records the response header added by extensions using the
+    DeclarativeNetRequest API. Recorded for each network request during the
+    OnHeadersReceived stage. Multiple samples can be recorded per request.
+  </summary>
+</histogram>
+
+<histogram name="Extensions.DeclarativeNetRequest.ResponseHeaderChanged"
+    enum="WebRequest.ResponseHeader" expires_after="2021-07-01">
+  <owner>karandeepb@chromium.org</owner>
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>kelvinjiang@chromium.org</owner>
+  <summary>
+    Records the response header modified by extensions using the
+    DeclarativeNetRequest API. Recorded for each network request during the
+    OnHeadersReceived stage. Multiple samples can be recorded per request.
+  </summary>
+</histogram>
+
+<histogram name="Extensions.DeclarativeNetRequest.ResponseHeaderRemoved"
+    enum="WebRequest.ResponseHeader" expires_after="2021-07-01">
+  <owner>karandeepb@chromium.org</owner>
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>kelvinjiang@chromium.org</owner>
+  <summary>
+    Records the response header removed by extensions using the
+    DeclarativeNetRequest API. Recorded for each network request during the
+    OnHeadersReceived stage. Multiple samples can be recorded per request.
+  </summary>
+</histogram>
+
 <histogram name="Extensions.DeclarativeNetRequest.RulesetReindexSuccessful"
     enum="BooleanSuccess" expires_after="2020-12-30">
   <owner>karandeepb@chromium.org</owner>
@@ -87473,6 +87581,16 @@
   </summary>
 </histogram>
 
+<histogram name="MobileDownload.DownloadLaterPromptStatus"
+    enum="DownloadLaterPromptStatus" expires_after="2021-06-01">
+  <owner>xingliu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
+  <summary>
+    Android: Records whether download later dialog will be shown to the user.
+    Recorded when the download later preference is initialized in native.
+  </summary>
+</histogram>
+
 <histogram name="MobileDownload.DownloadPromptStatus"
     enum="DownloadPromptStatus" expires_after="2021-04-01">
   <owner>xingliu@chromium.org</owner>
@@ -89082,6 +89200,9 @@
 
 <histogram name="MultiProfile.SigninUserUIPath"
     enum="MultiProfileSigninUserAction" expires_after="2020-08-23">
+  <obsolete>
+    Retired in M86.
+  </obsolete>
   <owner>skuhne@chromium.org</owner>
   <summary>
     Count the number of times each UI path is taken for signing into a new
@@ -123105,7 +123226,7 @@
 </histogram>
 
 <histogram name="PaintHolding.CommitTrigger2" enum="PaintHoldingCommitTrigger2"
-    expires_after="M86">
+    expires_after="2021-01-15">
   <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -123127,7 +123248,7 @@
 </histogram>
 
 <histogram name="PaintHolding.InputTiming2" enum="PaintHoldingInputTiming"
-    expires_after="M86">
+    expires_after="2021-01-15">
   <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -159280,7 +159401,7 @@
 </histogram>
 
 <histogram name="Settings.PrivacyElementInteractions"
-    enum="SettingsPrivacyElementInteractions" expires_after="M86">
+    enum="SettingsPrivacyElementInteractions" expires_after="M90">
   <owner>harrisonsean@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
   <owner>sauski@chromium.org</owner>
@@ -159302,14 +159423,14 @@
 </histogram>
 
 <histogram name="Settings.SafetyCheck.ExtensionsResult"
-    enum="SafetyCheckExtensionsStatus" expires_after="M86">
+    enum="SafetyCheckExtensionsStatus" expires_after="M90">
   <owner>andzaytsev@google.com</owner>
   <owner>msramek@chromium.org</owner>
   <summary>Resulting state of the safety check extensions check.</summary>
 </histogram>
 
 <histogram name="Settings.SafetyCheck.Interactions"
-    enum="SettingsSafetyCheckInteractions" expires_after="M86">
+    enum="SettingsSafetyCheckInteractions" expires_after="M90">
   <owner>rainhard@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
   <owner>anaudrey@chromium.org</owner>
@@ -159320,21 +159441,21 @@
 </histogram>
 
 <histogram name="Settings.SafetyCheck.PasswordsResult"
-    enum="SafetyCheckPasswordsStatus" expires_after="M86">
+    enum="SafetyCheckPasswordsStatus" expires_after="M90">
   <owner>andzaytsev@google.com</owner>
   <owner>msramek@chromium.org</owner>
   <summary>Resulting state of the safety check password check.</summary>
 </histogram>
 
 <histogram name="Settings.SafetyCheck.SafeBrowsingResult"
-    enum="SafetyCheckSafeBrowsingStatus" expires_after="M86">
+    enum="SafetyCheckSafeBrowsingStatus" expires_after="M90">
   <owner>andzaytsev@google.com</owner>
   <owner>msramek@chromium.org</owner>
   <summary>Resulting state of the safety check Safe Browsing check.</summary>
 </histogram>
 
 <histogram name="Settings.SafetyCheck.UpdatesResult"
-    enum="SafetyCheckUpdateStatus" expires_after="M86">
+    enum="SafetyCheckUpdateStatus" expires_after="M90">
   <owner>andzaytsev@google.com</owner>
   <owner>msramek@chromium.org</owner>
   <summary>Resulting state of the safety check updates check.</summary>
@@ -179635,10 +179756,10 @@
   <summary>Tracks UI events related to the translate bubble.</summary>
 </histogram>
 
-<histogram name="Translate.CaptureText" units="ms" expires_after="M86">
-  <owner>chrome-language@google.com</owner>
-  <owner>dougarnett@google.com</owner>
+<histogram name="Translate.CaptureText" units="ms" expires_after="M88">
+  <owner>sclittle@google.com</owner>
   <owner>megjablon@google.com</owner>
+  <owner>chrome-language@google.com</owner>
   <summary>
     The time spent capturing plain text from the DOM. Pre sub frame translation
     support (this includes before M83), this is reported by
@@ -179916,9 +180037,9 @@
 
 <histogram name="Translate.LanguageDetection.ContentLength" units="characters"
     expires_after="2020-12-01">
-  <owner>chrome-language@google.com</owner>
-  <owner>dougarnett@chromium.org</owner>
+  <owner>sclittle@chromium.org</owner>
   <owner>megjablon@chromium.org</owner>
+  <owner>chrome-language@google.com</owner>
   <summary>
     The number of characters of page content used for language detection.
   </summary>
@@ -179959,8 +180080,8 @@
 
 <histogram name="Translate.LanguageDeterminedDuration" units="ms"
     expires_after="2021-06-01">
-  <owner>dougarnett@chromium.org</owner>
   <owner>sclittle@chromium.org</owner>
+  <owner>megjablon@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
     Records the time from when a navigation finishes to when its page language
@@ -180324,9 +180445,9 @@
 
 <histogram name="Translate.Translate.AMPCacheURL" enum="BooleanTranslate"
     expires_after="2020-12-01">
-  <owner>chrome-language@google.com</owner>
-  <owner>dougarnett@google.com</owner>
+  <owner>sclittle@google.com</owner>
   <owner>megjablon@google.com</owner>
+  <owner>chrome-language@google.com</owner>
   <summary>
     The number of times the translate button was clicked in the translate
     infobar for a page that is likely an AMP Cache URL.
@@ -180334,10 +180455,10 @@
 </histogram>
 
 <histogram name="Translate.TranslateFrameCount" units="frames"
-    expires_after="M86">
-  <owner>chrome-language@google.com</owner>
-  <owner>dougarnett@google.com</owner>
+    expires_after="M88">
+  <owner>sclittle@google.com</owner>
   <owner>megjablon@google.com</owner>
+  <owner>chrome-language@google.com</owner>
   <summary>
     The number of frames translated for a translated page (including the main
     frame). This is recorded when sub frame translation is enabled.
@@ -180346,8 +180467,9 @@
 
 <histogram name="Translate.TranslateSubframe.ErrorType" enum="TranslateError"
     expires_after="2020-12-01">
-  <owner>dougarnett@google.com</owner>
+  <owner>sclittle@google.com</owner>
   <owner>megjablon@google.com</owner>
+  <owner>chrome-language@google.com</owner>
   <summary>
     The error type for a failed sub frame translation (where the main frame was
     successfully translated). This is recorded when sub frame translation is
@@ -180357,9 +180479,9 @@
 
 <histogram name="Translate.TranslateSubframe.SuccessPercentage" units="%"
     expires_after="2020-12-01">
-  <owner>chrome-language@google.com</owner>
-  <owner>dougarnett@google.com</owner>
+  <owner>sclittle@google.com</owner>
   <owner>megjablon@google.com</owner>
+  <owner>chrome-language@google.com</owner>
   <summary>
     The percentage of sub frames that translated successfully for a translated
     page (where the main frame was successfully translated). This is recorded
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 2a296652..2ea87e9 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -242,8 +242,10 @@
 # issues, please contact johnchen@chromium.org.
 BUILDERS = {
     'android-builder-perf': {
-        'additional_compile_targets':
-        ['microdump_stackwalk', 'angle_perftests', 'chrome_apk'],
+        'additional_compile_targets': [
+            'microdump_stackwalk', 'angle_perftests', 'chrome_apk',
+            'system_webview_google_apk'
+        ],
         'tests': [
             {
                 'name': 'resource_sizes_monochrome_minimal_apks',
@@ -289,8 +291,10 @@
         False,
     },
     'android_arm64-builder-perf': {
-        'additional_compile_targets':
-        ['microdump_stackwalk', 'angle_perftests', 'chrome_apk'],
+        'additional_compile_targets': [
+            'microdump_stackwalk', 'angle_perftests', 'chrome_apk',
+            'system_webview_google_apk'
+        ],
         'tests': [
             {
                 'name': 'resource_sizes_monochrome_minimal_apks',
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 83e5d5c0..6bf98a1 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,16 +1,16 @@
 {
     "trace_processor_shell": {
         "win": {
-            "hash": "1757ada10cf694a2703bbdaeee7130c2dd70f23a",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/7e4e0146560d0b1960105cd804c3b194f26419b2/trace_processor_shell.exe"
+            "hash": "9422fbaca127b736c7c04ceadaeec1ecb5435396",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/c4c3be736312e3ea8f454f323a98d3f14707bd72/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "e0fea6be741521bc0d5c7b5eb6cc12fd29ff979e",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/f224f744249c9165d83b480b8d94f43fb15b2da0/trace_processor_shell"
+            "hash": "ee514396d2e9a5059923db3926ac0a74ab0b9e12",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/c4c3be736312e3ea8f454f323a98d3f14707bd72/trace_processor_shell"
         },
         "linux": {
-            "hash": "7642799c9ae1ce8ebcf37728847e21dac68a93f7",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/f224f744249c9165d83b480b8d94f43fb15b2da0/trace_processor_shell"
+            "hash": "44a12287f9221a9a14a18507955ef53fa31f06f2",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/c4c3be736312e3ea8f454f323a98d3f14707bd72/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 4fbe5b4..29fa19e2 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -211,6 +211,7 @@
   deps = [
     "//base:base_java",
     "//components/payments/mojom:mojom_java",
+    "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_appcompat_appcompat_java",
     "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//third_party/android_deps:androidx_core_core_java",
@@ -375,6 +376,7 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/junit",
   ]
@@ -428,8 +430,10 @@
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
     "//base/test:test_support_java",
+    "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//third_party/android_deps:androidx_asynclayoutinflater_asynclayoutinflater_java",
+    "//third_party/android_deps:androidx_test_runner_java",
     "//third_party/hamcrest:hamcrest_java",
   ]
 }
@@ -494,6 +498,7 @@
     "//base:base_java_test_support",
     "//base:jni_java",
     "//content/public/test/android:content_java_test_support",
+    "//third_party/android_deps:androidx_test_runner_java",
     "//third_party/junit",
   ]
 }
diff --git a/ui/base/cursor/cursor_theme_manager.h b/ui/base/cursor/cursor_theme_manager.h
index a6d09f0..9ef31c0f 100644
--- a/ui/base/cursor/cursor_theme_manager.h
+++ b/ui/base/cursor/cursor_theme_manager.h
@@ -26,6 +26,9 @@
 
   void RemoveObserver(CursorThemeManagerObserver* observer);
 
+  virtual std::string GetCursorThemeName() = 0;
+  virtual int GetCursorThemeSize() = 0;
+
  protected:
   CursorThemeManager();
 
@@ -34,9 +37,6 @@
     return cursor_theme_observers_;
   }
 
-  virtual std::string GetCursorThemeName() = 0;
-  virtual int GetCursorThemeSize() = 0;
-
  private:
   base::ObserverList<CursorThemeManagerObserver> cursor_theme_observers_;
 };
diff --git a/ui/base/x/BUILD.gn b/ui/base/x/BUILD.gn
index 550d3c4..b98d099 100644
--- a/ui/base/x/BUILD.gn
+++ b/ui/base/x/BUILD.gn
@@ -19,6 +19,8 @@
     "x11_cursor.h",
     "x11_cursor_factory.cc",
     "x11_cursor_factory.h",
+    "x11_cursor_loader.cc",
+    "x11_cursor_loader.h",
     "x11_desktop_window_move_client.cc",
     "x11_desktop_window_move_client.h",
     "x11_display_manager.cc",
@@ -125,9 +127,13 @@
 
 source_set("unittests") {
   testonly = true
-  sources = [ "x11_cursor_factory_unittest.cc" ]
+  sources = [
+    "x11_cursor_factory_unittest.cc",
+    "x11_cursor_loader_unittest.cc",
+  ]
   deps = [
     ":x",
+    "//base",
     "//skia",
     "//testing/gtest",
     "//ui/gfx/geometry",
diff --git a/ui/base/x/x11_cursor.cc b/ui/base/x/x11_cursor.cc
index 9d55c5d..c483bd7 100644
--- a/ui/base/x/x11_cursor.cc
+++ b/ui/base/x/x11_cursor.cc
@@ -4,45 +4,39 @@
 
 #include "ui/base/x/x11_cursor.h"
 
-#include "base/check_op.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/x/x11_util.h"
-#include "ui/gfx/geometry/point.h"
+#include "ui/base/x/x11_cursor_loader.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/xproto.h"
 
 namespace ui {
 
-X11Cursor::X11Cursor(const SkBitmap& bitmap, const gfx::Point& hotspot) {
-  XcursorImage* image = SkBitmapToXcursorImage(bitmap, hotspot);
-  xcursor_ = XcursorImageLoadCursor(gfx::GetXDisplay(), image);
-  XcursorImageDestroy(image);
+X11Cursor::X11Cursor() = default;
+
+X11Cursor::X11Cursor(x11::Cursor cursor) : loaded_(true), xcursor_(cursor) {}
+
+void X11Cursor::OnCursorLoaded(Callback callback) {
+  if (loaded_)
+    std::move(callback).Run(xcursor_);
+  else
+    callbacks_.push_back(std::move(callback));
 }
 
-X11Cursor::X11Cursor(const std::vector<SkBitmap>& bitmaps,
-                     const gfx::Point& hotspot,
-                     int frame_delay_ms) {
-  // Initialize an XCursorImage for each frame, store all of them in
-  // XCursorImages and load the cursor from that.
-  XcursorImages* images = XcursorImagesCreate(bitmaps.size());
-  images->nimage = bitmaps.size();
-  for (size_t frame = 0; frame < bitmaps.size(); ++frame) {
-    XcursorImage* x_image = SkBitmapToXcursorImage(bitmaps[frame], hotspot);
-    x_image->delay = frame_delay_ms;
-    images->images[frame] = x_image;
-  }
-
-  xcursor_ = XcursorImagesLoadCursor(gfx::GetXDisplay(), images);
-  XcursorImagesDestroy(images);
+void X11Cursor::SetCursor(x11::Cursor cursor) {
+  DCHECK(!loaded_);
+  loaded_ = true;
+  xcursor_ = cursor;
+  for (auto& callback : callbacks_)
+    std::move(callback).Run(cursor);
+  callbacks_.clear();
 }
 
-X11Cursor::X11Cursor(::Cursor xcursor) : xcursor_(xcursor) {}
-
-// static
-scoped_refptr<X11Cursor> X11Cursor::CreateInvisible() {
-  return base::MakeRefCounted<X11Cursor>(CreateInvisibleCursor());
+x11::Cursor X11Cursor::ReleaseCursor() {
+  return std::exchange(xcursor_, x11::Cursor::None);
 }
 
 X11Cursor::~X11Cursor() {
-  XFreeCursor(gfx::GetXDisplay(), xcursor_);
+  if (xcursor_ != x11::Cursor::None)
+    x11::Connection::Get()->FreeCursor({xcursor_});
 }
 
 }  // namespace ui
diff --git a/ui/base/x/x11_cursor.h b/ui/base/x/x11_cursor.h
index 1711b94..dba2dbe 100644
--- a/ui/base/x/x11_cursor.h
+++ b/ui/base/x/x11_cursor.h
@@ -9,14 +9,7 @@
 
 #include "base/component_export.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/scoped_refptr.h"
-#include "ui/gfx/x/x11.h"
-
-class SkBitmap;
-
-namespace gfx {
-class Point;
-}
+#include "ui/gfx/x/xproto.h"
 
 namespace ui {
 
@@ -27,27 +20,34 @@
  public:
   REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
 
-  // Handles creating X11 cursor resources from SkBitmap/hotspot.
-  X11Cursor(const SkBitmap& bitmap, const gfx::Point& hotspot);
+  using Callback = base::OnceCallback<void(x11::Cursor)>;
+
+  X11Cursor();
+  explicit X11Cursor(x11::Cursor cursor);
+
   X11Cursor(const X11Cursor&) = delete;
   X11Cursor& operator=(const X11Cursor&) = delete;
-  X11Cursor(const std::vector<SkBitmap>& bitmaps,
-            const gfx::Point& hotspot,
-            int frame_delay_ms);
-  // Wraps an X11 cursor |xcursor|.
-  explicit X11Cursor(::Cursor xcursor);
 
-  // Creates a new cursor that is invisible.
-  static scoped_refptr<X11Cursor> CreateInvisible();
+  void OnCursorLoaded(Callback callback);
 
-  ::Cursor xcursor() const { return xcursor_; }
+  bool loaded() const { return loaded_; }
+  x11::Cursor xcursor() const { return xcursor_; }
 
  private:
   friend class base::RefCounted<X11Cursor>;
+  friend class XCursorLoader;
+
+  void SetCursor(x11::Cursor cursor);
+
+  // This cannot be named Release() since it conflicts with base::RefCounted.
+  x11::Cursor ReleaseCursor();
 
   ~X11Cursor();
 
-  ::Cursor xcursor_ = x11::None;
+  bool loaded_ = false;
+  x11::Cursor xcursor_ = x11::Cursor::None;
+
+  std::vector<Callback> callbacks_;
 };
 
 }  // namespace ui
diff --git a/ui/base/x/x11_cursor_factory.cc b/ui/base/x/x11_cursor_factory.cc
index 26624776..32d7b3b1 100644
--- a/ui/base/x/x11_cursor_factory.cc
+++ b/ui/base/x/x11_cursor_factory.cc
@@ -7,8 +7,10 @@
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
 #include "ui/base/x/x11_cursor.h"
+#include "ui/base/x/x11_cursor_loader.h"
 #include "ui/base/x/x11_util.h"
 #include "ui/gfx/geometry/point.h"
+#include "ui/gfx/x/connection.h"
 #include "ui/gfx/x/x11.h"
 
 namespace ui {
@@ -23,10 +25,130 @@
   return static_cast<PlatformCursor>(cursor);
 }
 
+scoped_refptr<X11Cursor> CreateInvisibleCursor(XCursorLoader* cursor_loader) {
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(1, 1);
+  return cursor_loader->CreateCursor(bitmap, gfx::Point(0, 0));
+}
+
+// Returns a cursor name, compatible with either X11 or the FreeDesktop.org
+// cursor spec
+// (https://www.x.org/releases/current/doc/libX11/libX11/libX11.html#x_font_cursors
+// and https://www.freedesktop.org/wiki/Specifications/cursor-spec/), followed
+// by fallbacks that can work as replacements in some environments where the
+// original may not be available (e.g. desktop environments other than
+// GNOME and KDE).
+// TODO(hferreiro): each list starts with the FreeDesktop.org icon name but
+// "ns-resize", "ew-resize", "nesw-resize", "nwse-resize", "grab", "grabbing",
+// which were not available in older versions of Breeze, the default KDE theme.
+std::vector<std::string> CursorNamesFromType(mojom::CursorType type) {
+  switch (type) {
+    case mojom::CursorType::kMove:
+      // Returning "move" is the correct thing here, but Blink doesn't make a
+      // distinction between move and all-scroll.  Other platforms use a cursor
+      // more consistent with all-scroll, so use that.
+    case mojom::CursorType::kMiddlePanning:
+    case mojom::CursorType::kMiddlePanningVertical:
+    case mojom::CursorType::kMiddlePanningHorizontal:
+      return {"all-scroll", "fleur"};
+    case mojom::CursorType::kEastPanning:
+    case mojom::CursorType::kEastResize:
+      return {"e-resize", "right_side"};
+    case mojom::CursorType::kNorthPanning:
+    case mojom::CursorType::kNorthResize:
+      return {"n-resize", "top_side"};
+    case mojom::CursorType::kNorthEastPanning:
+    case mojom::CursorType::kNorthEastResize:
+      return {"ne-resize", "top_right_corner"};
+    case mojom::CursorType::kNorthWestPanning:
+    case mojom::CursorType::kNorthWestResize:
+      return {"nw-resize", "top_left_corner"};
+    case mojom::CursorType::kSouthPanning:
+    case mojom::CursorType::kSouthResize:
+      return {"s-resize", "bottom_side"};
+    case mojom::CursorType::kSouthEastPanning:
+    case mojom::CursorType::kSouthEastResize:
+      return {"se-resize", "bottom_right_corner"};
+    case mojom::CursorType::kSouthWestPanning:
+    case mojom::CursorType::kSouthWestResize:
+      return {"sw-resize", "bottom_left_corner"};
+    case mojom::CursorType::kWestPanning:
+    case mojom::CursorType::kWestResize:
+      return {"w-resize", "left_side"};
+    case mojom::CursorType::kNone:
+      return {"none"};
+    case mojom::CursorType::kGrab:
+      return {"openhand", "grab"};
+    case mojom::CursorType::kGrabbing:
+      return {"closedhand", "grabbing", "hand2"};
+    case mojom::CursorType::kCross:
+      return {"crosshair", "cross"};
+    case mojom::CursorType::kHand:
+      return {"pointer", "hand", "hand2"};
+    case mojom::CursorType::kIBeam:
+      return {"text", "xterm"};
+    case mojom::CursorType::kProgress:
+      return {"progress", "left_ptr_watch", "watch"};
+    case mojom::CursorType::kWait:
+      return {"wait", "watch"};
+    case mojom::CursorType::kHelp:
+      return {"help"};
+    case mojom::CursorType::kNorthSouthResize:
+      return {"sb_v_double_arrow", "ns-resize"};
+    case mojom::CursorType::kEastWestResize:
+      return {"sb_h_double_arrow", "ew-resize"};
+    case mojom::CursorType::kColumnResize:
+      return {"col-resize", "sb_h_double_arrow"};
+    case mojom::CursorType::kRowResize:
+      return {"row-resize", "sb_v_double_arrow"};
+    case mojom::CursorType::kNorthEastSouthWestResize:
+      return {"size_bdiag", "nesw-resize", "fd_double_arrow"};
+    case mojom::CursorType::kNorthWestSouthEastResize:
+      return {"size_fdiag", "nwse-resize", "bd_double_arrow"};
+    case mojom::CursorType::kVerticalText:
+      return {"vertical-text"};
+    case mojom::CursorType::kZoomIn:
+      return {"zoom-in"};
+    case mojom::CursorType::kZoomOut:
+      return {"zoom-out"};
+    case mojom::CursorType::kCell:
+      return {"cell", "plus"};
+    case mojom::CursorType::kContextMenu:
+      return {"context-menu"};
+    case mojom::CursorType::kAlias:
+      return {"alias"};
+    case mojom::CursorType::kNoDrop:
+      return {"no-drop"};
+    case mojom::CursorType::kCopy:
+      return {"copy"};
+    case mojom::CursorType::kNotAllowed:
+      return {"not-allowed", "crossed_circle"};
+    case mojom::CursorType::kDndNone:
+      return {"dnd-none", "hand2"};
+    case mojom::CursorType::kDndMove:
+      return {"dnd-move", "hand2"};
+    case mojom::CursorType::kDndCopy:
+      return {"dnd-copy", "hand2"};
+    case mojom::CursorType::kDndLink:
+      return {"dnd-link", "hand2"};
+    case mojom::CursorType::kCustom:
+      // kCustom is for custom image cursors. The platform cursor will be set
+      // at WebCursor::GetPlatformCursor().
+      NOTREACHED();
+      FALLTHROUGH;
+    case mojom::CursorType::kNull:
+    case mojom::CursorType::kPointer:
+      return {"left_ptr"};
+  }
+  NOTREACHED();
+  return {"left_ptr"};
+}
+
 }  // namespace
 
 X11CursorFactory::X11CursorFactory()
-    : invisible_cursor_(X11Cursor::CreateInvisible()) {}
+    : cursor_loader_(std::make_unique<XCursorLoader>(x11::Connection::Get())),
+      invisible_cursor_(CreateInvisibleCursor(cursor_loader_.get())) {}
 
 X11CursorFactory::~X11CursorFactory() = default;
 
@@ -53,7 +175,7 @@
     return ToPlatformCursor(invisible_cursor_.get());
   }
 
-  auto cursor = base::MakeRefCounted<X11Cursor>(bitmap, hotspot);
+  auto cursor = cursor_loader_->CreateCursor(bitmap, hotspot);
   cursor->AddRef();
   return ToPlatformCursor(cursor.get());
 }
@@ -62,8 +184,11 @@
     const std::vector<SkBitmap>& bitmaps,
     const gfx::Point& hotspot,
     int frame_delay_ms) {
-  auto cursor =
-      base::MakeRefCounted<X11Cursor>(bitmaps, hotspot, frame_delay_ms);
+  std::vector<XCursorLoader::Image> images;
+  images.reserve(bitmaps.size());
+  for (const auto& bitmap : bitmaps)
+    images.push_back(XCursorLoader::Image{bitmap, hotspot, frame_delay_ms});
+  auto cursor = cursor_loader_->CreateCursor(images);
   cursor->AddRef();
   return ToPlatformCursor(cursor.get());
 }
@@ -84,12 +209,10 @@
 
 void X11CursorFactory::OnCursorThemeNameChanged(
     const std::string& cursor_theme_name) {
-  XcursorSetTheme(gfx::GetXDisplay(), cursor_theme_name.c_str());
   ClearThemeCursors();
 }
 
 void X11CursorFactory::OnCursorThemeSizeChanged(int cursor_theme_size) {
-  XcursorSetDefaultSize(gfx::GetXDisplay(), cursor_theme_size);
   ClearThemeCursors();
 }
 
@@ -100,11 +223,8 @@
 
   if (!default_cursors_.count(type)) {
     // Try to load a predefined X11 cursor.
-    ::Cursor xcursor = LoadCursorFromType(type);
-    if (xcursor == x11::None)
-      return nullptr;
-    auto cursor = base::MakeRefCounted<X11Cursor>(xcursor);
-    default_cursors_[type] = cursor;
+    default_cursors_[type] =
+        cursor_loader_->LoadCursor(CursorNamesFromType(type));
   }
 
   // Returns owned default cursor for this type.
diff --git a/ui/base/x/x11_cursor_factory.h b/ui/base/x/x11_cursor_factory.h
index 42451185..464c75f 100644
--- a/ui/base/x/x11_cursor_factory.h
+++ b/ui/base/x/x11_cursor_factory.h
@@ -19,6 +19,7 @@
 
 namespace ui {
 class X11Cursor;
+class XCursorLoader;
 
 // CursorFactoryOzone implementation for X11 cursors.
 class COMPONENT_EXPORT(UI_BASE_X) X11CursorFactory
@@ -49,6 +50,8 @@
 
   void ClearThemeCursors();
 
+  std::unique_ptr<XCursorLoader> cursor_loader_;
+
   // Loads/caches default cursor or returns cached version.
   scoped_refptr<X11Cursor> GetDefaultCursorInternal(mojom::CursorType type);
 
diff --git a/ui/base/x/x11_cursor_loader.cc b/ui/base/x/x11_cursor_loader.cc
new file mode 100644
index 0000000..72f823e
--- /dev/null
+++ b/ui/base/x/x11_cursor_loader.cc
@@ -0,0 +1,548 @@
+// 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 "ui/base/x/x11_cursor_loader.h"
+
+#include <limits>
+#include <string>
+
+#include "base/bind.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequence_checker.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/sys_byteorder.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/task_runner_util.h"
+#include "ui/base/cursor/cursor_theme_manager.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/xproto.h"
+
+namespace ui {
+
+namespace {
+
+// These cursor names are indexed by their ID in a cursor font.
+constexpr const char* cursor_names[] = {
+    "X_cursor",
+    "arrow",
+    "based_arrow_down",
+    "based_arrow_up",
+    "boat",
+    "bogosity",
+    "bottom_left_corner",
+    "bottom_right_corner",
+    "bottom_side",
+    "bottom_tee",
+    "box_spiral",
+    "center_ptr",
+    "circle",
+    "clock",
+    "coffee_mug",
+    "cross",
+    "cross_reverse",
+    "crosshair",
+    "diamond_cross",
+    "dot",
+    "dotbox",
+    "double_arrow",
+    "draft_large",
+    "draft_small",
+    "draped_box",
+    "exchange",
+    "fleur",
+    "gobbler",
+    "gumby",
+    "hand1",
+    "hand2",
+    "heart",
+    "icon",
+    "iron_cross",
+    "left_ptr",
+    "left_side",
+    "left_tee",
+    "leftbutton",
+    "ll_angle",
+    "lr_angle",
+    "man",
+    "middlebutton",
+    "mouse",
+    "pencil",
+    "pirate",
+    "plus",
+    "question_arrow",
+    "right_ptr",
+    "right_side",
+    "right_tee",
+    "rightbutton",
+    "rtl_logo",
+    "sailboat",
+    "sb_down_arrow",
+    "sb_h_double_arrow",
+    "sb_left_arrow",
+    "sb_right_arrow",
+    "sb_up_arrow",
+    "sb_v_double_arrow",
+    "shuttle",
+    "sizing",
+    "spider",
+    "spraycan",
+    "star",
+    "target",
+    "tcross",
+    "top_left_arrow",
+    "top_left_corner",
+    "top_right_corner",
+    "top_side",
+    "top_tee",
+    "trek",
+    "ul_angle",
+    "umbrella",
+    "ur_angle",
+    "watch",
+    "xterm",
+};
+
+std::string GetEnv(const std::string& var) {
+  auto env = base::Environment::Create();
+  std::string value;
+  env->GetVar(var, &value);
+  return value;
+}
+
+std::string CursorPath() {
+  constexpr const char kDefaultPath[] =
+      "~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons";
+  std::string path = GetEnv("XCURSOR_PATH");
+  return path.empty() ? kDefaultPath : path;
+}
+
+x11::Render::PictFormat GetRenderARGBFormat(
+    const x11::Render::QueryPictFormatsReply& formats) {
+  for (const auto& format : formats.formats) {
+    if (format.type == x11::Render::PictType::Direct && format.depth == 32 &&
+        format.direct.alpha_shift == 24 && format.direct.alpha_mask == 0xff &&
+        format.direct.red_shift == 16 && format.direct.red_mask == 0xff &&
+        format.direct.green_shift == 8 && format.direct.green_mask == 0xff &&
+        format.direct.blue_shift == 0 && format.direct.blue_mask == 0xff) {
+      return format.id;
+    }
+  }
+  return {};
+}
+
+std::vector<std::string> GetBaseThemes(const base::FilePath& abspath) {
+  DCHECK(abspath.IsAbsolute());
+  constexpr const char kKeyInherits[] = "Inherits";
+  std::string contents;
+  base::ReadFileToString(abspath, &contents);
+  base::StringPairs pairs;
+  base::SplitStringIntoKeyValuePairs(contents, '=', '\n', &pairs);
+  for (const auto& pair : pairs) {
+    if (base::TrimWhitespaceASCII(pair.first, base::TRIM_ALL) == kKeyInherits) {
+      return base::SplitString(pair.second, ",;", base::TRIM_WHITESPACE,
+                               base::SPLIT_WANT_NONEMPTY);
+    }
+  }
+  return {};
+}
+
+base::FilePath CanonicalizePath(base::FilePath path) {
+  std::vector<std::string> components;
+  path.GetComponents(&components);
+  if (components[0] == "~") {
+    path = base::GetHomeDir();
+    for (size_t i = 1; i < components.size(); i++)
+      path = path.Append(components[i]);
+  } else {
+    path = base::MakeAbsoluteFilePath(path);
+  }
+  return path;
+}
+
+// Reads the cursor called |name| for the theme named |theme|. Searches  all
+// paths in the XCursor path and parent themes.
+scoped_refptr<base::RefCountedMemory> ReadCursorFromTheme(
+    const std::string& theme,
+    const std::string& name) {
+  constexpr const char kCursorDir[] = "cursors";
+  constexpr const char kThemeInfo[] = "index.theme";
+  std::vector<std::string> base_themes;
+
+  auto paths = base::SplitString(CursorPath(), ":", base::TRIM_WHITESPACE,
+                                 base::SPLIT_WANT_NONEMPTY);
+  for (const auto& path : paths) {
+    auto dir = CanonicalizePath(base::FilePath(path));
+    if (dir.empty())
+      continue;
+    base::FilePath theme_dir = dir.Append(theme);
+    base::FilePath cursor_dir = theme_dir.Append(kCursorDir);
+
+    std::string contents;
+    if (base::ReadFileToString(cursor_dir.Append(name), &contents))
+      return base::RefCountedString::TakeString(&contents);
+
+    if (base_themes.empty())
+      base_themes = GetBaseThemes(theme_dir.Append(kThemeInfo));
+  }
+
+  for (const auto& path : base_themes) {
+    if (auto contents = ReadCursorFromTheme(path, name))
+      return contents;
+  }
+
+  return nullptr;
+}
+
+scoped_refptr<base::RefCountedMemory> ReadCursorFile(
+    const std::string& name,
+    const std::string& rm_xcursor_theme) {
+  constexpr const char kDefaultTheme[] = "default";
+  std::string themes[] = {
+      // The toolkit theme has the highest priority.
+      CursorThemeManager::GetInstance()
+          ? CursorThemeManager::GetInstance()->GetCursorThemeName()
+          : std::string(),
+
+      // Next try Xcursor.theme.
+      rm_xcursor_theme,
+
+      // As a last resort, use the default theme.
+      kDefaultTheme,
+  };
+
+  for (const std::string& theme : themes) {
+    if (theme.empty())
+      continue;
+    if (auto file = ReadCursorFromTheme(theme, name))
+      return file;
+  }
+  return nullptr;
+}
+
+std::vector<XCursorLoader::Image> ReadCursorImages(
+    const std::vector<std::string>& names,
+    const std::string& rm_xcursor_theme,
+    uint32_t preferred_size) {
+  // Fallback on a left pointer if possible.
+  auto names_copy = names;
+  names_copy.push_back("left_ptr");
+  for (const auto& name : names_copy) {
+    if (auto contents = ReadCursorFile(name, rm_xcursor_theme)) {
+      auto images = ParseCursorFile(contents, preferred_size);
+      if (!images.empty())
+        return images;
+    }
+  }
+  return {};
+}
+
+}  // namespace
+
+XCursorLoader::XCursorLoader(x11::Connection* connection)
+    : connection_(connection) {
+  auto ver_cookie = connection_->render().QueryVersion(
+      {x11::Render::major_version, x11::Render::minor_version});
+  auto pf_cookie = connection_->render().QueryPictFormats({});
+  cursor_font_ = connection_->GenerateId<x11::Font>();
+  connection_->OpenFont({cursor_font_, "cursor"});
+
+  std::string resource_manager;
+  if (ui::GetStringProperty(connection_->default_root(), "RESOURCE_MANAGER",
+                            &resource_manager)) {
+    ParseXResources(resource_manager);
+  }
+
+  if (auto reply = ver_cookie.Sync()) {
+    render_version_ =
+        base::Version({reply->major_version, reply->minor_version});
+  }
+
+  if (auto pf_reply = pf_cookie.Sync())
+    pict_format_ = GetRenderARGBFormat(*pf_reply.reply);
+
+  for (uint16_t i = 0; i < base::size(cursor_names); i++)
+    cursor_name_to_char_[cursor_names[i]] = i;
+}
+
+XCursorLoader::~XCursorLoader() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+scoped_refptr<X11Cursor> XCursorLoader::LoadCursor(
+    const std::vector<std::string>& names) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto cursor = base::MakeRefCounted<X11Cursor>();
+  if (SupportsCreateCursor()) {
+    base::PostTaskAndReplyWithResult(
+        FROM_HERE,
+        {base::ThreadPool(), base::MayBlock(),
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+        base::BindOnce(ReadCursorImages, names, rm_xcursor_theme_,
+                       GetPreferredCursorSize()),
+        base::BindOnce(&XCursorLoader::LoadCursorImpl,
+                       weak_factory_.GetWeakPtr(), cursor, names));
+  } else {
+    LoadCursorImpl(cursor, names, {});
+  }
+  return cursor;
+}
+
+scoped_refptr<X11Cursor> XCursorLoader::CreateCursor(
+    const std::vector<Image>& images) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  std::vector<scoped_refptr<X11Cursor>> cursors;
+  std::vector<x11::Render::AnimationCursorElement> elements;
+  cursors.reserve(images.size());
+  elements.reserve(images.size());
+
+  for (const Image& image : images) {
+    auto cursor = CreateCursor(image.bitmap, image.hotspot);
+    cursors.push_back(cursor);
+    elements.push_back(x11::Render::AnimationCursorElement{
+        cursor->xcursor_, image.frame_delay_ms});
+  }
+
+  if (elements.empty())
+    return nullptr;
+  if (elements.size() == 1 || !SupportsCreateAnimCursor())
+    return cursors[0];
+
+  auto cursor = connection_->GenerateId<x11::Cursor>();
+  connection_->render().CreateAnimCursor({cursor, elements});
+  return base::MakeRefCounted<X11Cursor>(cursor);
+}
+
+scoped_refptr<X11Cursor> XCursorLoader::CreateCursor(
+    const SkBitmap& bitmap,
+    const gfx::Point& hotspot) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto pixmap = connection_->GenerateId<x11::Pixmap>();
+  auto gc = connection_->GenerateId<x11::GraphicsContext>();
+  int width = bitmap.width();
+  int height = bitmap.height();
+  connection_->CreatePixmap(
+      {32, pixmap, connection_->default_root(), width, height});
+  connection_->CreateGC({gc, pixmap});
+
+  size_t size = bitmap.computeByteSize();
+  std::vector<uint8_t> vec(size);
+  memcpy(vec.data(), bitmap.getPixels(), size);
+  auto* connection = x11::Connection::Get();
+  connection->PutImage({
+      .format = x11::ImageFormat::ZPixmap,
+      .drawable = static_cast<x11::Pixmap>(pixmap),
+      .gc = static_cast<x11::GraphicsContext>(gc),
+      .width = width,
+      .height = height,
+      .depth = 32,
+      .data = base::RefCountedBytes::TakeVector(&vec),
+  });
+
+  x11::Render::Picture pic = connection_->GenerateId<x11::Render::Picture>();
+  connection_->render().CreatePicture({pic, pixmap, pict_format_});
+
+  auto cursor = connection_->GenerateId<x11::Cursor>();
+  connection_->render().CreateCursor({cursor, pic, hotspot.x(), hotspot.y()});
+
+  connection_->render().FreePicture({pic});
+  connection_->FreePixmap({pixmap});
+  connection_->FreeGC({gc});
+
+  return base::MakeRefCounted<X11Cursor>(cursor);
+}
+
+void XCursorLoader::LoadCursorImpl(
+    scoped_refptr<X11Cursor> cursor,
+    const std::vector<std::string>& names,
+    const std::vector<XCursorLoader::Image>& images) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto xcursor = connection_->GenerateId<x11::Cursor>();
+  if (!images.empty()) {
+    xcursor = CreateCursor(images)->ReleaseCursor();
+  } else {
+    // Fallback to using a font cursor.
+    auto core_char = CursorNamesToChar(names);
+    connection_->CreateGlyphCursor({xcursor, cursor_font_, cursor_font_,
+                                    2 * core_char, 2 * core_char + 1, 0, 0, 0,
+                                    65535, 65535, 65535});
+  }
+  cursor->SetCursor(xcursor);
+}
+
+uint32_t XCursorLoader::GetPreferredCursorSize() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  constexpr const char kXcursorSizeEnv[] = "XCURSOR_SIZE";
+  constexpr unsigned int kCursorSizeInchNum = 16;
+  constexpr unsigned int kCursorSizeInchDen = 72;
+  constexpr unsigned int kScreenCursorRatio = 48;
+
+  // Allow the XCURSOR_SIZE environment variable to override GTK settings.
+  int size;
+  if (base::StringToInt(GetEnv(kXcursorSizeEnv), &size) && size > 0)
+    return size;
+
+  // Let the toolkit have the next say.
+  auto* manager = CursorThemeManager::GetInstance();
+  size = manager ? manager->GetCursorThemeSize() : 0;
+  if (size > 0)
+    return size;
+
+  // Use Xcursor.size from RESOURCE_MANAGER if available.
+  if (rm_xcursor_size_)
+    return rm_xcursor_size_;
+
+  // Guess the cursor size based on the DPI.
+  if (rm_xft_dpi_)
+    return rm_xft_dpi_ * kCursorSizeInchNum / kCursorSizeInchDen;
+
+  // As a last resort, guess the cursor size based on the screen size.
+  const auto& screen = connection_->default_screen();
+  return std::min(screen.width_in_pixels, screen.height_in_pixels) /
+         kScreenCursorRatio;
+}
+
+void XCursorLoader::ParseXResources(const std::string& resources) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::StringPairs pairs;
+  base::SplitStringIntoKeyValuePairs(resources, ':', '\n', &pairs);
+  for (const auto& pair : pairs) {
+    auto key = base::TrimWhitespaceASCII(pair.first, base::TRIM_ALL);
+    auto value = base::TrimWhitespaceASCII(pair.second, base::TRIM_ALL);
+
+    if (key == "Xcursor.theme")
+      rm_xcursor_theme_ = std::string(value);
+    else if (key == "Xcursor.size")
+      base::StringToUint(value, &rm_xcursor_size_);
+    else if (key == "Xft.dpi")
+      base::StringToUint(value, &rm_xft_dpi_);
+  }
+}
+
+uint16_t XCursorLoader::CursorNamesToChar(
+    const std::vector<std::string>& names) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  for (const auto& name : names) {
+    auto it = cursor_name_to_char_.find(name);
+    if (it != cursor_name_to_char_.end())
+      return it->second;
+  }
+  // Use a left pointer as a fallback.
+  return 0;
+}
+
+bool XCursorLoader::SupportsCreateCursor() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return render_version_ >= base::Version("0.5");
+}
+
+bool XCursorLoader::SupportsCreateAnimCursor() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return render_version_ >= base::Version("0.8");
+}
+
+// This is ported from libxcb-cursor's parse_cursor_file.c:
+// https://gitlab.freedesktop.org/xorg/lib/libxcb-cursor/-/blob/master/cursor/parse_cursor_file.c
+std::vector<XCursorLoader::Image> ParseCursorFile(
+    scoped_refptr<base::RefCountedMemory> file,
+    uint32_t preferred_size) {
+  constexpr uint32_t kMagic = 0x72756358;
+  constexpr uint32_t kImageType = 0xfffd0002;
+
+  const uint8_t* mem = file->data();
+  size_t offset = 0;
+
+  auto ReadU32s = [&](void* dest, size_t len) {
+    DCHECK_EQ(len % 4, 0u);
+    if (offset + len > file->size())
+      return false;
+    const auto* src32 = reinterpret_cast<const uint32_t*>(mem + offset);
+    auto* dest32 = reinterpret_cast<uint32_t*>(dest);
+    for (size_t i = 0; i < len / 4; i++)
+      dest32[i] = base::ByteSwapToLE32(src32[i]);
+    offset += len;
+    return true;
+  };
+
+  struct FileHeader {
+    uint32_t magic;
+    uint32_t header;
+    uint32_t version;
+    uint32_t ntoc;
+  } header;
+  if (!ReadU32s(&header, sizeof(FileHeader)) || header.magic != kMagic)
+    return {};
+
+  struct TableOfContentsEntry {
+    uint32_t type;
+    uint32_t subtype;
+    uint32_t position;
+  };
+  std::vector<TableOfContentsEntry> toc;
+  toc.reserve(header.ntoc);
+  for (uint32_t i = 0; i < header.ntoc; i++) {
+    TableOfContentsEntry entry;
+    if (!ReadU32s(&entry, sizeof(TableOfContentsEntry)))
+      return {};
+    toc.push_back(entry);
+  }
+
+  uint32_t best_size = std::numeric_limits<uint32_t>::max();
+  for (const auto& entry : toc) {
+    auto delta = [](uint32_t x, uint32_t y) {
+      return std::max(x, y) - std::min(x, y);
+    };
+    if (entry.type != kImageType)
+      continue;
+    if (delta(entry.subtype, preferred_size) < delta(best_size, preferred_size))
+      best_size = entry.subtype;
+  }
+
+  std::vector<XCursorLoader::Image> images;
+  for (const auto& entry : toc) {
+    if (entry.type != kImageType || entry.subtype != best_size)
+      continue;
+    offset = entry.position;
+    struct ChunkHeader {
+      uint32_t header;
+      uint32_t type;
+      uint32_t subtype;
+      uint32_t version;
+    } chunk_header;
+    if (!ReadU32s(&chunk_header, sizeof(ChunkHeader)) ||
+        chunk_header.type != entry.type ||
+        chunk_header.subtype != entry.subtype) {
+      continue;
+    }
+
+    struct ImageHeader {
+      uint32_t width;
+      uint32_t height;
+      uint32_t xhot;
+      uint32_t yhot;
+      uint32_t delay;
+    } image;
+    if (!ReadU32s(&image, sizeof(ImageHeader)))
+      continue;
+    SkBitmap bitmap;
+    bitmap.allocN32Pixels(image.width, image.height);
+    if (!ReadU32s(bitmap.getPixels(), bitmap.computeByteSize()))
+      continue;
+    images.push_back(XCursorLoader::Image{
+        bitmap, gfx::Point(image.xhot, image.yhot), image.delay});
+  }
+  return images;
+}
+
+}  // namespace ui
diff --git a/ui/base/x/x11_cursor_loader.h b/ui/base/x/x11_cursor_loader.h
new file mode 100644
index 0000000..851f5245
--- /dev/null
+++ b/ui/base/x/x11_cursor_loader.h
@@ -0,0 +1,92 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_X_X11_CURSOR_LOADER_H_
+#define UI_BASE_X_X11_CURSOR_LOADER_H_
+
+#include <unordered_map>
+
+#include "base/component_export.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/version.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/x/x11_cursor.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/x/render.h"
+#include "ui/gfx/x/xproto.h"
+
+namespace ui {
+
+// This is a port of libxcursor.
+class COMPONENT_EXPORT(UI_BASE_X) XCursorLoader {
+ public:
+  struct Image {
+    SkBitmap bitmap;
+    gfx::Point hotspot;
+    int frame_delay_ms;
+  };
+
+  explicit XCursorLoader(x11::Connection* connection);
+
+  ~XCursorLoader();
+
+  // Loads a system cursor for the given |names|. The cursor is loaded
+  // asynchronously, but some cursor (possibly a fallback) is always guaranteed
+  // to be loaded.
+  scoped_refptr<X11Cursor> LoadCursor(const std::vector<std::string>& names);
+
+  // Synchronously loads a cursor for the given |bitmap| or |images|. nullptr
+  // may be returned if image cursors are not supported.
+  scoped_refptr<X11Cursor> CreateCursor(const std::vector<Image>& images);
+  scoped_refptr<X11Cursor> CreateCursor(const SkBitmap& bitmap,
+                                        const gfx::Point& hotspot);
+
+ private:
+  friend class XCursorLoaderTest;
+
+  void LoadCursorImpl(scoped_refptr<X11Cursor> cursor,
+                      const std::vector<std::string>& names,
+                      const std::vector<XCursorLoader::Image>& images);
+
+  uint32_t GetPreferredCursorSize() const;
+
+  // Populate the |rm_*| variables from the value of the RESOURCE_MANAGER
+  // property on the root window.
+  void ParseXResources(const std::string& resources);
+
+  uint16_t CursorNamesToChar(const std::vector<std::string>& names) const;
+
+  bool SupportsCreateCursor() const;
+  bool SupportsCreateAnimCursor() const;
+
+  x11::Connection* connection_ = nullptr;
+
+  x11::Font cursor_font_ = x11::Font::None;
+
+  x11::Render::PictFormat pict_format_{};
+
+  // Values obtained from the RESOURCE_MANAGER property on the root window.
+  std::string rm_xcursor_theme_;
+  unsigned int rm_xcursor_size_ = 0;
+  unsigned int rm_xft_dpi_ = 0;
+
+  base::Version render_version_;
+
+  std::unordered_map<std::string, uint16_t> cursor_name_to_char_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<XCursorLoader> weak_factory_{this};
+};
+
+COMPONENT_EXPORT(UI_BASE_X)
+std::vector<XCursorLoader::Image> ParseCursorFile(
+    scoped_refptr<base::RefCountedMemory> file,
+    uint32_t preferred_size);
+
+}  // namespace ui
+
+#endif  // UI_BASE_X_X11_CURSOR_LOADER_H_
diff --git a/ui/base/x/x11_cursor_loader_unittest.cc b/ui/base/x/x11_cursor_loader_unittest.cc
new file mode 100644
index 0000000..52a75c0
--- /dev/null
+++ b/ui/base/x/x11_cursor_loader_unittest.cc
@@ -0,0 +1,262 @@
+// 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 "ui/base/x/x11_cursor_loader.h"
+
+#undef Bool
+
+#include "base/memory/ref_counted_memory.h"
+#include "base/sys_byteorder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+
+namespace {
+std::vector<XCursorLoader::Image> ParseFile(std::vector<uint32_t>* data,
+                                            uint32_t preferred_size) {
+  for (uint32_t& i : *data)
+    i = base::ByteSwapToLE32(i);
+  std::vector<uint8_t> vec(data->size() * sizeof(uint32_t));
+  memcpy(vec.data(), data->data(), vec.size());
+  return ParseCursorFile(base::RefCountedBytes::TakeVector(&vec),
+                         preferred_size);
+}
+
+}  // namespace
+
+TEST(XCursorLoaderTest, Basic) {
+  std::vector<uint32_t> file{
+      // magic number
+      0x72756358,
+      // bytes in header
+      28,
+      // version
+      1,
+      // number of TOC entries
+      1,
+
+      // type (image)
+      0xfffd0002,
+      // subtype (image size)
+      1,
+      // position
+      28,
+
+      // bytes in header
+      16,
+      // chunk type (image)
+      0xfffd0002,
+      // chunk subtype (image size)
+      1,
+      // chunk type version
+      1,
+      // image width
+      1,
+      // image height
+      1,
+      // xhot
+      1234,
+      // yhot
+      5678,
+      // delay
+      123,
+      // chunk data (ARGB image)
+      0xff123456,
+  };
+  auto images = ParseFile(&file, 1);
+  ASSERT_EQ(images.size(), 1ul);
+  EXPECT_EQ(images[0].frame_delay_ms, 123);
+  EXPECT_EQ(images[0].bitmap.width(), 1);
+  EXPECT_EQ(images[0].bitmap.height(), 1);
+  EXPECT_EQ(images[0].hotspot.x(), 1234);
+  EXPECT_EQ(images[0].hotspot.y(), 5678);
+  EXPECT_EQ(images[0].bitmap.getColor(0, 0), 0xff123456);
+}
+
+TEST(XCursorLoaderTest, BestSize) {
+  std::vector<uint32_t> file{
+      // magic number
+      0x72756358,
+      // bytes in header
+      28,
+      // version
+      1,
+      // number of TOC entries
+      3,
+
+      // type (image)
+      0xfffd0002,
+      // subtype (image size)
+      1,
+      // position
+      52,
+
+      // type (image)
+      0xfffd0002,
+      // subtype (image size)
+      2,
+      // position
+      92,
+
+      // type (image)
+      0xfffd0002,
+      // subtype (image size)
+      3,
+      // position
+      144,
+
+      // bytes in header
+      16,
+      // chunk type (image)
+      0xfffd0002,
+      // chunk subtype (image size)
+      1,
+      // chunk type version
+      1,
+      // image width
+      1,
+      // image height
+      1,
+      // xhot
+      0,
+      // yhot
+      0,
+      // delay
+      0,
+      // chunk data (ARGB image)
+      0xffffffff,
+
+      // bytes in header
+      16,
+      // chunk type (image)
+      0xfffd0002,
+      // chunk subtype (image size)
+      2,
+      // chunk type version
+      1,
+      // image width
+      2,
+      // image height
+      2,
+      // xhot
+      0,
+      // yhot
+      0,
+      // delay
+      0,
+      // chunk data (ARGB image)
+      0xffffffff,
+      0xffffffff,
+      0xffffffff,
+      0xffffffff,
+
+      // bytes in header
+      16,
+      // chunk type (image)
+      0xfffd0002,
+      // chunk subtype (image size)
+      3,
+      // chunk type version
+      1,
+      // image width
+      3,
+      // image height
+      3,
+      // xhot
+      0,
+      // yhot
+      0,
+      // delay
+      0,
+      // chunk data (ARGB image)
+      0xffffffff,
+      0xffffffff,
+      0xffffffff,
+      0xffffffff,
+      0xffffffff,
+      0xffffffff,
+      0xffffffff,
+      0xffffffff,
+      0xffffffff,
+  };
+  auto images = ParseFile(&file, 2);
+  ASSERT_EQ(images.size(), 1ul);
+  EXPECT_EQ(images[0].bitmap.width(), 2);
+  EXPECT_EQ(images[0].bitmap.height(), 2);
+}
+
+TEST(XCursorLoaderTest, Animated) {
+  std::vector<uint32_t> file{
+      // magic number
+      0x72756358,
+      // bytes in header
+      28,
+      // version
+      1,
+      // number of TOC entries
+      2,
+
+      // type (image)
+      0xfffd0002,
+      // subtype (image size)
+      1,
+      // position
+      40,
+
+      // type (image)
+      0xfffd0002,
+      // subtype (image size)
+      1,
+      // position
+      80,
+
+      // bytes in header
+      16,
+      // chunk type (image)
+      0xfffd0002,
+      // chunk subtype (image size)
+      1,
+      // chunk type version
+      1,
+      // image width
+      1,
+      // image height
+      1,
+      // xhot
+      0,
+      // yhot
+      0,
+      // delay
+      500,
+      // chunk data (ARGB image)
+      0xff123456,
+
+      // bytes in header
+      16,
+      // chunk type (image)
+      0xfffd0002,
+      // chunk subtype (image size)
+      1,
+      // chunk type version
+      1,
+      // image width
+      1,
+      // image height
+      1,
+      // xhot
+      0,
+      // yhot
+      0,
+      // delay
+      500,
+      // chunk data (ARGB image)
+      0xff123456,
+  };
+  auto images = ParseFile(&file, 1);
+  ASSERT_EQ(images.size(), 2ul);
+  EXPECT_EQ(images[0].frame_delay_ms, 500);
+  EXPECT_EQ(images[1].frame_delay_ms, 500);
+}
+
+}  // namespace ui
diff --git a/ui/base/x/x11_move_loop.h b/ui/base/x/x11_move_loop.h
index 24000a9a3..9a004554 100644
--- a/ui/base/x/x11_move_loop.h
+++ b/ui/base/x/x11_move_loop.h
@@ -11,22 +11,24 @@
 
 namespace ui {
 
+class X11Cursor;
+
 // Runs a nested run loop and grabs the mouse. This is used to implement
 // dragging.
 class X11MoveLoop {
  public:
-  virtual ~X11MoveLoop() {}
+  virtual ~X11MoveLoop() = default;
 
   // Runs the nested run loop. While the mouse is grabbed, use |cursor| as
   // the mouse cursor. Returns true if the move-loop is completed successfully.
   // If the pointer-grab fails, or the move-loop is canceled by the user (e.g.
   // by pressing escape), then returns false.
   virtual bool RunMoveLoop(bool can_grab_pointer,
-                           ::Cursor old_cursor,
-                           ::Cursor new_cursor) = 0;
+                           scoped_refptr<ui::X11Cursor> old_cursor,
+                           scoped_refptr<ui::X11Cursor> new_cursor) = 0;
 
   // Updates the cursor while the move loop is running.
-  virtual void UpdateCursor(::Cursor cursor) = 0;
+  virtual void UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) = 0;
 
   // Ends the move loop that's currently in progress.
   virtual void EndMoveLoop() = 0;
diff --git a/ui/base/x/x11_pointer_grab.cc b/ui/base/x/x11_pointer_grab.cc
index b7a65240c..96216a5 100644
--- a/ui/base/x/x11_pointer_grab.cc
+++ b/ui/base/x/x11_pointer_grab.cc
@@ -4,10 +4,14 @@
 
 #include "ui/base/x/x11_pointer_grab.h"
 
+#include "base/bind.h"
+#include "base/cancelable_callback.h"
 #include "base/check.h"
+#include "base/no_destructor.h"
 #include "ui/base/x/x11_util.h"
 #include "ui/events/devices/x11/device_data_manager_x11.h"
 #include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/xproto.h"
 
 namespace ui {
 
@@ -19,9 +23,14 @@
 // The "owner events" parameter used to grab the pointer.
 bool g_owner_events = false;
 
-}  // namespace
+base::CancelableOnceCallback<void(x11::Cursor)>& GetGrabCallback() {
+  static base::NoDestructor<base::CancelableOnceCallback<void(x11::Cursor)>>
+      callback;
+  return *callback;
+}
 
-int GrabPointer(x11::Window window, bool owner_events, ::Cursor cursor) {
+int GrabPointerImpl(x11::Window window, bool owner_events, x11::Cursor cursor) {
+  GetGrabCallback().Cancel();
   int result = GrabInvalidTime;
   if (ui::IsXInput2Available()) {
     // Do an XInput2 pointer grab. If there is an active XInput2 pointer grab
@@ -42,10 +51,10 @@
         ui::DeviceDataManagerX11::GetInstance()->master_pointers();
     for (int master_pointer : master_pointers) {
       evmask.deviceid = master_pointer;
-      result =
-          XIGrabDevice(gfx::GetXDisplay(), master_pointer,
-                       static_cast<uint32_t>(window), x11::CurrentTime, cursor,
-                       GrabModeAsync, GrabModeAsync, owner_events, &evmask);
+      result = XIGrabDevice(gfx::GetXDisplay(), master_pointer,
+                            static_cast<uint32_t>(window), x11::CurrentTime,
+                            static_cast<uint32_t>(cursor), GrabModeAsync,
+                            GrabModeAsync, owner_events, &evmask);
       // Assume that the grab will succeed on either all or none of the master
       // pointers.
       if (result != GrabSuccess) {
@@ -59,7 +68,8 @@
     int event_mask = PointerMotionMask | ButtonReleaseMask | ButtonPressMask;
     result = XGrabPointer(gfx::GetXDisplay(), static_cast<uint32_t>(window),
                           owner_events, event_mask, GrabModeAsync,
-                          GrabModeAsync, x11::None, cursor, x11::CurrentTime);
+                          GrabModeAsync, x11::None,
+                          static_cast<uint32_t>(cursor), x11::CurrentTime);
   }
 
   if (result == GrabSuccess) {
@@ -69,12 +79,30 @@
   return result;
 }
 
-void ChangeActivePointerGrabCursor(::Cursor cursor) {
+}  // namespace
+
+int GrabPointer(x11::Window window,
+                bool owner_events,
+                scoped_refptr<ui::X11Cursor> cursor) {
+  if (!cursor)
+    return GrabPointerImpl(window, owner_events, x11::Cursor::None);
+  if (cursor->loaded())
+    return GrabPointerImpl(window, owner_events, cursor->xcursor());
+
+  int result = GrabPointerImpl(window, owner_events, x11::Cursor::None);
+  GetGrabCallback().Reset(base::BindOnce(base::IgnoreResult(GrabPointerImpl),
+                                         window, owner_events));
+  cursor->OnCursorLoaded(GetGrabCallback().callback());
+  return result;
+}
+
+void ChangeActivePointerGrabCursor(scoped_refptr<ui::X11Cursor> cursor) {
   DCHECK(g_grab_window != x11::Window::None);
   GrabPointer(g_grab_window, g_owner_events, cursor);
 }
 
 void UngrabPointer() {
+  GetGrabCallback().Cancel();
   g_grab_window = x11::Window::None;
   if (ui::IsXInput2Available()) {
     const std::vector<int>& master_pointers =
diff --git a/ui/base/x/x11_pointer_grab.h b/ui/base/x/x11_pointer_grab.h
index 8df8140..b4794a61 100644
--- a/ui/base/x/x11_pointer_grab.h
+++ b/ui/base/x/x11_pointer_grab.h
@@ -8,17 +8,20 @@
 #include "base/component_export.h"
 #include "ui/gfx/x/x11_types.h"
 
-typedef unsigned long Cursor;
-
 namespace ui {
 
+class X11Cursor;
+
 // Grabs the pointer. It is unnecessary to ungrab the pointer prior to grabbing
 // it.
 COMPONENT_EXPORT(UI_BASE_X)
-int GrabPointer(x11::Window window, bool owner_events, ::Cursor cursor);
+int GrabPointer(x11::Window window,
+                bool owner_events,
+                scoped_refptr<ui::X11Cursor> cursor);
 
 // Sets the cursor to use for the duration of the active pointer grab.
-COMPONENT_EXPORT(UI_BASE_X) void ChangeActivePointerGrabCursor(::Cursor cursor);
+COMPONENT_EXPORT(UI_BASE_X)
+void ChangeActivePointerGrabCursor(scoped_refptr<ui::X11Cursor> cursor);
 
 // Ungrabs the pointer.
 COMPONENT_EXPORT(UI_BASE_X) void UngrabPointer();
diff --git a/ui/base/x/x11_util.cc b/ui/base/x/x11_util.cc
index 784ad13d..58e6b5a3 100644
--- a/ui/base/x/x11_util.cc
+++ b/ui/base/x/x11_util.cc
@@ -22,6 +22,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
+#include "base/debug/stack_trace.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted_memory.h"
@@ -46,6 +47,8 @@
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkTypes.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
+#include "ui/base/x/x11_cursor.h"
+#include "ui/base/x/x11_cursor_loader.h"
 #include "ui/base/x/x11_menu_list.h"
 #include "ui/events/devices/x11/device_data_manager_x11.h"
 #include "ui/events/devices/x11/touch_factory_x11.h"
@@ -163,219 +166,6 @@
   return !err_tracker.FoundNewError() && result;
 }
 
-unsigned int GetMaxCursorSize() {
-  constexpr unsigned int kQuerySize = std::numeric_limits<uint16_t>::max();
-  auto* connection = x11::Connection::Get();
-  x11::QueryBestSizeRequest request{
-      x11::QueryShapeOf::LargestCursor,
-      static_cast<x11::Window>(GetX11RootWindow()), kQuerySize, kQuerySize};
-  if (auto response = connection->QueryBestSize(request).Sync())
-    return std::min(response->width, response->height);
-  // libXcursor defines MAX_BITMAP_CURSOR_SIZE to 64 in src/xcursorint.h, so use
-  // this as a fallback in case the X server returns zero size, which can happen
-  // on some buggy implementations of XWayland/XMir.
-  return 64;
-}
-
-// A process wide singleton cache for custom X cursors.
-class XCustomCursorCache {
- public:
-  static XCustomCursorCache* GetInstance() {
-    return base::Singleton<XCustomCursorCache>::get();
-  }
-
-  ::Cursor InstallCustomCursor(XcursorImage* image) {
-    XCustomCursor* custom_cursor = new XCustomCursor(image);
-    ::Cursor xcursor = custom_cursor->cursor();
-    cache_[xcursor] = custom_cursor;
-    return xcursor;
-  }
-
-  void Ref(::Cursor cursor) { cache_[cursor]->Ref(); }
-
-  void Unref(::Cursor cursor) {
-    if (cache_[cursor]->Unref())
-      cache_.erase(cursor);
-  }
-
-  void Clear() { cache_.clear(); }
-
-  const XcursorImage* GetXcursorImage(::Cursor cursor) const {
-    return cache_.find(cursor)->second->image();
-  }
-
- private:
-  friend struct base::DefaultSingletonTraits<XCustomCursorCache>;
-
-  class XCustomCursor {
-   public:
-    // This takes ownership of the image.
-    explicit XCustomCursor(XcursorImage* image) : image_(image), ref_(1) {
-      cursor_ = XcursorImageLoadCursor(gfx::GetXDisplay(), image);
-    }
-
-    ~XCustomCursor() {
-      XcursorImageDestroy(image_);
-      XFreeCursor(gfx::GetXDisplay(), cursor_);
-    }
-
-    ::Cursor cursor() const { return cursor_; }
-
-    void Ref() { ++ref_; }
-
-    // Returns true if the cursor was destroyed because of the unref.
-    bool Unref() {
-      if (--ref_ == 0) {
-        delete this;
-        return true;
-      }
-      return false;
-    }
-
-    const XcursorImage* image() const { return image_; }
-
-   private:
-    XcursorImage* image_;
-    int ref_;
-    ::Cursor cursor_;
-
-    DISALLOW_COPY_AND_ASSIGN(XCustomCursor);
-  };
-
-  XCustomCursorCache() = default;
-  ~XCustomCursorCache() { Clear(); }
-
-  std::map<::Cursor, XCustomCursor*> cache_;
-  DISALLOW_COPY_AND_ASSIGN(XCustomCursorCache);
-};
-
-// Converts a SKBitmap to unpremul alpha.
-SkBitmap ConvertSkBitmapToUnpremul(const SkBitmap& bitmap) {
-  DCHECK_NE(bitmap.alphaType(), kUnpremul_SkAlphaType);
-
-  SkImageInfo image_info = SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
-                                                kUnpremul_SkAlphaType);
-  SkBitmap converted_bitmap;
-  converted_bitmap.allocPixels(image_info);
-  bitmap.readPixels(image_info, converted_bitmap.getPixels(),
-                    image_info.minRowBytes(), 0, 0);
-
-  return converted_bitmap;
-}
-
-// Returns a cursor name, compatible with either X11 or the FreeDesktop.org
-// cursor spec
-// (https://www.x.org/releases/current/doc/libX11/libX11/libX11.html#x_font_cursors
-// and https://www.freedesktop.org/wiki/Specifications/cursor-spec/), followed
-// by fallbacks that can work as replacements in some environments where the
-// original may not be available (e.g. desktop environments other than
-// GNOME and KDE).
-// TODO(hferreiro): each list starts with the FreeDesktop.org icon name but
-// "ns-resize", "ew-resize", "nesw-resize", "nwse-resize", "grab", "grabbing",
-// which were not available in older versions of Breeze, the default KDE theme.
-std::vector<const char*> CursorNamesFromType(mojom::CursorType type) {
-  switch (type) {
-    case mojom::CursorType::kMove:
-      // Returning "move" is the correct thing here, but Blink doesn't make a
-      // distinction between move and all-scroll.  Other platforms use a cursor
-      // more consistent with all-scroll, so use that.
-    case mojom::CursorType::kMiddlePanning:
-    case mojom::CursorType::kMiddlePanningVertical:
-    case mojom::CursorType::kMiddlePanningHorizontal:
-      return {"all-scroll", "fleur"};
-    case mojom::CursorType::kEastPanning:
-    case mojom::CursorType::kEastResize:
-      return {"e-resize", "right_side"};
-    case mojom::CursorType::kNorthPanning:
-    case mojom::CursorType::kNorthResize:
-      return {"n-resize", "top_side"};
-    case mojom::CursorType::kNorthEastPanning:
-    case mojom::CursorType::kNorthEastResize:
-      return {"ne-resize", "top_right_corner"};
-    case mojom::CursorType::kNorthWestPanning:
-    case mojom::CursorType::kNorthWestResize:
-      return {"nw-resize", "top_left_corner"};
-    case mojom::CursorType::kSouthPanning:
-    case mojom::CursorType::kSouthResize:
-      return {"s-resize", "bottom_side"};
-    case mojom::CursorType::kSouthEastPanning:
-    case mojom::CursorType::kSouthEastResize:
-      return {"se-resize", "bottom_right_corner"};
-    case mojom::CursorType::kSouthWestPanning:
-    case mojom::CursorType::kSouthWestResize:
-      return {"sw-resize", "bottom_left_corner"};
-    case mojom::CursorType::kWestPanning:
-    case mojom::CursorType::kWestResize:
-      return {"w-resize", "left_side"};
-    case mojom::CursorType::kNone:
-      return {"none"};
-    case mojom::CursorType::kGrab:
-      return {"openhand", "grab"};
-    case mojom::CursorType::kGrabbing:
-      return {"closedhand", "grabbing", "hand2"};
-    case mojom::CursorType::kCross:
-      return {"crosshair", "cross"};
-    case mojom::CursorType::kHand:
-      return {"pointer", "hand", "hand2"};
-    case mojom::CursorType::kIBeam:
-      return {"text", "xterm"};
-    case mojom::CursorType::kProgress:
-      return {"progress", "left_ptr_watch", "watch"};
-    case mojom::CursorType::kWait:
-      return {"wait", "watch"};
-    case mojom::CursorType::kHelp:
-      return {"help"};
-    case mojom::CursorType::kNorthSouthResize:
-      return {"sb_v_double_arrow", "ns-resize"};
-    case mojom::CursorType::kEastWestResize:
-      return {"sb_h_double_arrow", "ew-resize"};
-    case mojom::CursorType::kColumnResize:
-      return {"col-resize", "sb_h_double_arrow"};
-    case mojom::CursorType::kRowResize:
-      return {"row-resize", "sb_v_double_arrow"};
-    case mojom::CursorType::kNorthEastSouthWestResize:
-      return {"size_bdiag", "nesw-resize", "fd_double_arrow"};
-    case mojom::CursorType::kNorthWestSouthEastResize:
-      return {"size_fdiag", "nwse-resize", "bd_double_arrow"};
-    case mojom::CursorType::kVerticalText:
-      return {"vertical-text"};
-    case mojom::CursorType::kZoomIn:
-      return {"zoom-in"};
-    case mojom::CursorType::kZoomOut:
-      return {"zoom-out"};
-    case mojom::CursorType::kCell:
-      return {"cell", "plus"};
-    case mojom::CursorType::kContextMenu:
-      return {"context-menu"};
-    case mojom::CursorType::kAlias:
-      return {"alias"};
-    case mojom::CursorType::kNoDrop:
-      return {"no-drop"};
-    case mojom::CursorType::kCopy:
-      return {"copy"};
-    case mojom::CursorType::kNotAllowed:
-      return {"not-allowed", "crossed_circle"};
-    case mojom::CursorType::kDndNone:
-      return {"dnd-none", "hand2"};
-    case mojom::CursorType::kDndMove:
-      return {"dnd-move", "hand2"};
-    case mojom::CursorType::kDndCopy:
-      return {"dnd-copy", "hand2"};
-    case mojom::CursorType::kDndLink:
-      return {"dnd-link", "hand2"};
-    case mojom::CursorType::kCustom:
-      // kCustom is for custom image cursors. The platform cursor will be set
-      // at WebCursor::GetPlatformCursor().
-      NOTREACHED();
-      FALLTHROUGH;
-    case mojom::CursorType::kNull:
-    case mojom::CursorType::kPointer:
-      return {"left_ptr"};
-  }
-  NOTREACHED();
-  return {"left_ptr"};
-}
-
 }  // namespace
 
 void DeleteProperty(x11::Window window, x11::Atom name) {
@@ -554,75 +344,6 @@
   return event_base;
 }
 
-::Cursor CreateReffedCustomXCursor(XcursorImage* image) {
-  return XCustomCursorCache::GetInstance()->InstallCustomCursor(image);
-}
-
-void RefCustomXCursor(::Cursor cursor) {
-  XCustomCursorCache::GetInstance()->Ref(cursor);
-}
-
-void UnrefCustomXCursor(::Cursor cursor) {
-  XCustomCursorCache::GetInstance()->Unref(cursor);
-}
-
-XcursorImage* SkBitmapToXcursorImage(const SkBitmap& cursor_image,
-                                     const gfx::Point& hotspot) {
-  // TODO(crbug.com/596782): It is possible for cursor_image to be zeroed out
-  // at this point, which leads to benign debug errors. Once this is fixed, we
-  // should  DCHECK_EQ(cursor_image.colorType(), kN32_SkColorType).
-
-  // X11 expects bitmap with unpremul alpha. If bitmap is premul then convert,
-  // otherwise semi-transparent parts of cursor will look strange.
-  const SkBitmap converted = (cursor_image.alphaType() != kUnpremul_SkAlphaType)
-                                 ? ConvertSkBitmapToUnpremul(cursor_image)
-                                 : cursor_image;
-
-  gfx::Point hotspot_point = hotspot;
-  SkBitmap scaled;
-
-  // X11 seems to have issues with cursors when images get larger than 64
-  // pixels. So rescale the image if necessary.
-  static const float kMaxPixel = GetMaxCursorSize();
-  bool needs_scale = false;
-  if (converted.width() > kMaxPixel || converted.height() > kMaxPixel) {
-    float scale = 1.f;
-    if (converted.width() > converted.height())
-      scale = kMaxPixel / converted.width();
-    else
-      scale = kMaxPixel / converted.height();
-
-    scaled = skia::ImageOperations::Resize(
-        converted, skia::ImageOperations::RESIZE_BETTER,
-        static_cast<int>(converted.width() * scale),
-        static_cast<int>(converted.height() * scale));
-    hotspot_point = gfx::ScaleToFlooredPoint(hotspot, scale);
-    needs_scale = true;
-  }
-
-  const SkBitmap& bitmap = needs_scale ? scaled : converted;
-  XcursorImage* image = XcursorImageCreate(bitmap.width(), bitmap.height());
-  image->xhot = std::min(bitmap.width() - 1, hotspot_point.x());
-  image->yhot = std::min(bitmap.height() - 1, hotspot_point.y());
-
-  if (bitmap.width() && bitmap.height()) {
-    // The |bitmap| contains ARGB image, so just copy it.
-    memcpy(image->pixels, bitmap.getPixels(),
-           bitmap.width() * bitmap.height() * 4);
-  }
-
-  return image;
-}
-
-::Cursor LoadCursorFromType(mojom::CursorType type) {
-  for (auto* name : CursorNamesFromType(type)) {
-    ::Cursor cursor = XcursorLibraryLoadCursor(gfx::GetXDisplay(), name);
-    if (cursor != x11::None)
-      return cursor;
-  }
-  return x11::None;
-}
-
 int CoalescePendingMotionEvents(const x11::Event* x11_event,
                                 x11::Event* last_event) {
   const auto* motion = x11_event->As<x11::MotionNotifyEvent>();
@@ -694,27 +415,6 @@
   return num_coalesced;
 }
 
-void HideHostCursor() {
-  static base::NoDestructor<XScopedCursor> invisible_cursor(
-      CreateInvisibleCursor(), gfx::GetXDisplay());
-  XDefineCursor(gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()),
-                invisible_cursor->get());
-}
-
-::Cursor CreateInvisibleCursor() {
-  XDisplay* xdisplay = gfx::GetXDisplay();
-  ::Cursor invisible_cursor;
-  char nodata[] = {0, 0, 0, 0, 0, 0, 0, 0};
-  XColor black;
-  black.red = black.green = black.blue = 0;
-  Pixmap blank = XCreateBitmapFromData(xdisplay, DefaultRootWindow(xdisplay),
-                                       nodata, 8, 8);
-  invisible_cursor =
-      XCreatePixmapCursor(xdisplay, blank, blank, &black, &black, 0, 0);
-  XFreePixmap(xdisplay, blank);
-  return invisible_cursor;
-}
-
 void SetUseOSWindowFrame(x11::Window window, bool use_os_window_frame) {
   // This data structure represents additional hints that we send to the window
   // manager and has a direct lineage back to Motif, which defined this de facto
@@ -1428,34 +1128,10 @@
 
 XRefcountedMemory::~XRefcountedMemory() = default;
 
-XScopedCursor::XScopedCursor(::Cursor cursor, XDisplay* display)
-    : cursor_(cursor), display_(display) {}
-
-XScopedCursor::~XScopedCursor() {
-  reset(0U);
-}
-
-::Cursor XScopedCursor::get() const {
-  return cursor_;
-}
-
-void XScopedCursor::reset(::Cursor cursor) {
-  if (cursor_)
-    XFreeCursor(display_, cursor_);
-  cursor_ = cursor;
-}
-
 void XImageDeleter::operator()(XImage* image) const {
   XDestroyImage(image);
 }
 
-namespace test {
-
-const XcursorImage* GetCachedXcursorImage(::Cursor cursor) {
-  return XCustomCursorCache::GetInstance()->GetXcursorImage(cursor);
-}
-}  // namespace test
-
 void SetX11ErrorHandlers(XErrorHandler error_handler,
                          XIOErrorHandler io_error_handler) {
   XSetErrorHandler(error_handler ? error_handler : DefaultX11ErrorHandler);
diff --git a/ui/base/x/x11_util.h b/ui/base/x/x11_util.h
index 5e4b5538..f4417eb 100644
--- a/ui/base/x/x11_util.h
+++ b/ui/base/x/x11_util.h
@@ -25,7 +25,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
-#include "ui/base/cursor/mojom/cursor_type.mojom-forward.h"
+#include "ui/base/x/x11_cursor.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/platform_event.h"
@@ -48,7 +48,6 @@
 class Point;
 class Rect;
 }  // namespace gfx
-class SkBitmap;
 
 namespace ui {
 
@@ -272,41 +271,12 @@
 // Returns the first event ID for the MIT-SHM extension, if available.
 COMPONENT_EXPORT(UI_BASE_X) int ShmEventBase();
 
-// Creates a custom X cursor from the image. This takes ownership of image. The
-// caller must not free/modify the image. The refcount of the newly created
-// cursor is set to 1.
-COMPONENT_EXPORT(UI_BASE_X)::Cursor
-    CreateReffedCustomXCursor(XcursorImage* image);
-
-// Increases the refcount of the custom cursor.
-COMPONENT_EXPORT(UI_BASE_X) void RefCustomXCursor(::Cursor cursor);
-
-// Decreases the refcount of the custom cursor, and destroys it if it reaches 0.
-COMPONENT_EXPORT(UI_BASE_X) void UnrefCustomXCursor(::Cursor cursor);
-
-// Creates a XcursorImage and copies the SkBitmap |bitmap| on it. Caller owns
-// the returned object.
-COMPONENT_EXPORT(UI_BASE_X)
-XcursorImage* SkBitmapToXcursorImage(const SkBitmap& bitmap,
-                                     const gfx::Point& hotspot);
-
-// Loads and returns an X11 cursor, trying to find one that matches |type|. If
-// unavailable, x11::None is returned.
-COMPONENT_EXPORT(UI_BASE_X)
-::Cursor LoadCursorFromType(mojom::CursorType type);
-
 // Coalesce all pending motion events (touch or mouse) that are at the top of
 // the queue, and return the number eliminated, storing the last one in
 // |last_event|.
 COMPONENT_EXPORT(UI_BASE_X)
 int CoalescePendingMotionEvents(const x11::Event* xev, x11::Event* last_event);
 
-// Hides the host cursor.
-COMPONENT_EXPORT(UI_BASE_X) void HideHostCursor();
-
-// Returns an invisible cursor.
-COMPONENT_EXPORT(UI_BASE_X)::Cursor CreateInvisibleCursor();
-
 // Sets whether |window| should use the OS window frame.
 COMPONENT_EXPORT(UI_BASE_X)
 void SetUseOSWindowFrame(x11::Window window, bool use_os_window_frame);
@@ -588,24 +558,6 @@
   DISALLOW_COPY_AND_ASSIGN(XRefcountedMemory);
 };
 
-// Keeps track of a cursor returned by an X function and makes sure it's
-// XFreeCursor'd.
-class COMPONENT_EXPORT(UI_BASE_X) XScopedCursor {
- public:
-  // Keeps track of |cursor| created with |display|.
-  XScopedCursor(::Cursor cursor, XDisplay* display);
-  ~XScopedCursor();
-
-  ::Cursor get() const;
-  void reset(::Cursor cursor);
-
- private:
-  ::Cursor cursor_;
-  XDisplay* display_;
-
-  DISALLOW_COPY_AND_ASSIGN(XScopedCursor);
-};
-
 struct COMPONENT_EXPORT(UI_BASE_X) XImageDeleter {
   void operator()(XImage* image) const;
 };
@@ -693,14 +645,6 @@
   DISALLOW_COPY_AND_ASSIGN(XVisualManager);
 };
 
-namespace test {
-
-// Returns the cached XcursorImage for |cursor|.
-COMPONENT_EXPORT(UI_BASE_X)
-const XcursorImage* GetCachedXcursorImage(::Cursor cursor);
-
-}  // namespace test
-
 }  // namespace ui
 
 #endif  // UI_BASE_X_X11_UTIL_H_
diff --git a/ui/base/x/x11_whole_screen_move_loop.cc b/ui/base/x/x11_whole_screen_move_loop.cc
index 39f4d0c..c1bdce6 100644
--- a/ui/base/x/x11_whole_screen_move_loop.cc
+++ b/ui/base/x/x11_whole_screen_move_loop.cc
@@ -52,7 +52,6 @@
 X11WholeScreenMoveLoop::X11WholeScreenMoveLoop(X11MoveLoopDelegate* delegate)
     : delegate_(delegate),
       in_move_loop_(false),
-      initial_cursor_(x11::None),
       grab_input_window_(x11::Window::None),
       grabbed_pointer_(false),
       canceled_(false) {}
@@ -131,9 +130,10 @@
   return ui::POST_DISPATCH_PERFORM_DEFAULT;
 }
 
-bool X11WholeScreenMoveLoop::RunMoveLoop(bool can_grab_pointer,
-                                         ::Cursor old_cursor,
-                                         ::Cursor new_cursor) {
+bool X11WholeScreenMoveLoop::RunMoveLoop(
+    bool can_grab_pointer,
+    scoped_refptr<ui::X11Cursor> old_cursor,
+    scoped_refptr<ui::X11Cursor> new_cursor) {
   DCHECK(!in_move_loop_);  // Can only handle one nested loop at a time.
 
   // Query the mouse cursor prior to the move loop starting so that it can be
@@ -181,7 +181,7 @@
   return !canceled_;
 }
 
-void X11WholeScreenMoveLoop::UpdateCursor(::Cursor cursor) {
+void X11WholeScreenMoveLoop::UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) {
   if (in_move_loop_)
     ui::ChangeActivePointerGrabCursor(cursor);
 }
@@ -219,7 +219,7 @@
   std::move(quit_closure_).Run();
 }
 
-bool X11WholeScreenMoveLoop::GrabPointer(::Cursor cursor) {
+bool X11WholeScreenMoveLoop::GrabPointer(scoped_refptr<X11Cursor> cursor) {
   auto* connection = x11::Connection::Get();
 
   // Pass "owner_events" as false so that X sends all mouse events to
diff --git a/ui/base/x/x11_whole_screen_move_loop.h b/ui/base/x/x11_whole_screen_move_loop.h
index eae75d85f..7717b3d3 100644
--- a/ui/base/x/x11_whole_screen_move_loop.h
+++ b/ui/base/x/x11_whole_screen_move_loop.h
@@ -42,15 +42,15 @@
 
   // X11MoveLoop:
   bool RunMoveLoop(bool can_grab_pointer,
-                   ::Cursor old_cursor,
-                   ::Cursor new_cursor) override;
-  void UpdateCursor(::Cursor cursor) override;
+                   scoped_refptr<ui::X11Cursor> old_cursor,
+                   scoped_refptr<ui::X11Cursor> new_cursor) override;
+  void UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) override;
   void EndMoveLoop() override;
 
  private:
   // Grabs the pointer, setting the mouse cursor to |cursor|. Returns true if
   // successful.
-  bool GrabPointer(::Cursor cursor);
+  bool GrabPointer(scoped_refptr<X11Cursor> cursor);
 
   void GrabEscKey();
 
@@ -70,7 +70,7 @@
 
   // Cursor in use prior to the move loop starting. Restored when the move loop
   // quits.
-  ::Cursor initial_cursor_;
+  scoped_refptr<X11Cursor> initial_cursor_ = nullptr;
 
   // An invisible InputOnly window. Keyboard grab and sometimes mouse grab
   // are set on this window.
diff --git a/ui/base/x/x11_window.cc b/ui/base/x/x11_window.cc
index 540c2cd..48c2590 100644
--- a/ui/base/x/x11_window.cc
+++ b/ui/base/x/x11_window.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <vector>
 
+#include "base/bind.h"
 #include "base/location.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_number_conversions.h"
@@ -692,7 +693,7 @@
 void XWindow::GrabPointer() {
   // If the pointer is already in |xwindow_|, we will not get a crossing event
   // with a mode of NotifyGrab, so we must record the grab state manually.
-  has_pointer_grab_ |= !ui::GrabPointer(xwindow_, true, x11::None);
+  has_pointer_grab_ |= !ui::GrabPointer(xwindow_, true, nullptr);
 }
 
 void XWindow::ReleasePointerGrab() {
@@ -732,9 +733,11 @@
   RaiseWindow(xwindow_);
 }
 
-void XWindow::SetCursor(::Cursor cursor) {
+void XWindow::SetCursor(scoped_refptr<X11Cursor> cursor) {
   last_cursor_ = cursor;
-  DefineCursor(xwindow_, static_cast<x11::Cursor>(cursor));
+  on_cursor_loaded_.Reset(base::BindOnce(DefineCursor, xwindow_));
+  if (cursor)
+    cursor->OnCursorLoaded(on_cursor_loaded_.callback());
 }
 
 bool XWindow::SetTitle(base::string16 title) {
@@ -1449,7 +1452,7 @@
   if (remap) {
     Map();
     if (has_pointer_grab_)
-      ChangeActivePointerGrabCursor(x11::None);
+      ChangeActivePointerGrabCursor(nullptr);
   }
 }
 
diff --git a/ui/base/x/x11_window.h b/ui/base/x/x11_window.h
index 73340d8..af282d419 100644
--- a/ui/base/x/x11_window.h
+++ b/ui/base/x/x11_window.h
@@ -16,6 +16,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/strings/string16.h"
+#include "ui/base/x/x11_cursor.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -38,6 +39,7 @@
 
 class Event;
 class XScopedEventSelector;
+class X11Cursor;
 
 ////////////////////////////////////////////////////////////////////////////////
 // XWindow class
@@ -128,7 +130,7 @@
   bool IsFullscreen() const;
   gfx::Rect GetOuterBounds() const;
 
-  void SetCursor(::Cursor cursor);
+  void SetCursor(scoped_refptr<X11Cursor> cursor);
   bool SetTitle(base::string16 title);
   void SetXWindowOpacity(float opacity);
   void SetXWindowAspectRatio(const gfx::SizeF& aspect_ratio);
@@ -178,7 +180,7 @@
   x11::Sync::Counter extended_update_counter() const {
     return extended_update_counter_;
   }
-  ::Cursor last_cursor() const { return last_cursor_; }
+  scoped_refptr<X11Cursor> last_cursor() const { return last_cursor_; }
 
  protected:
   // Updates |xwindow_|'s _NET_WM_USER_TIME if |xwindow_| is active.
@@ -392,7 +394,9 @@
   bool has_pointer_barriers_ = false;
   std::array<x11::XFixes::Barrier, 4> pointer_barriers_;
 
-  ::Cursor last_cursor_ = x11::None;
+  scoped_refptr<X11Cursor> last_cursor_;
+
+  base::CancelableOnceCallback<void(x11::Cursor)> on_cursor_loaded_;
 };
 
 }  // namespace ui
diff --git a/ui/display/fake/fake_display_delegate.cc b/ui/display/fake/fake_display_delegate.cc
index fd13e9e7..767a901 100644
--- a/ui/display/fake/fake_display_delegate.cc
+++ b/ui/display/fake/fake_display_delegate.cc
@@ -130,34 +130,36 @@
 }
 
 void FakeDisplayDelegate::Configure(
-    const display::DisplayConfigurationParams& display_config_params,
+    const std::vector<display::DisplayConfigurationParams>& config_requests,
     ConfigureCallback callback) {
-  bool configure_success = false;
+  base::flat_map<int64_t, bool> statuses;
+  for (const auto& config : config_requests) {
+    bool configure_success = false;
 
-  if (display_config_params.mode.has_value()) {
-    // Find display snapshot of display ID.
-    auto snapshot = find_if(
-        displays_.begin(), displays_.end(),
-        [&display_config_params](std::unique_ptr<DisplaySnapshot>& snapshot) {
-          return snapshot->display_id() == display_config_params.id;
-        });
-    if (snapshot != displays_.end()) {
-      // Check that config mode is appropriate for the display snapshot.
-      for (const auto& existing_mode : snapshot->get()->modes()) {
-        if (AreModesEqual(*existing_mode.get(),
-                          *display_config_params.mode.value().get())) {
-          configure_success = true;
-          break;
+    if (config.mode.has_value()) {
+      // Find display snapshot of display ID.
+      auto snapshot =
+          find_if(displays_.begin(), displays_.end(),
+                  [&config](std::unique_ptr<DisplaySnapshot>& snapshot) {
+                    return snapshot->display_id() == config.id;
+                  });
+      if (snapshot != displays_.end()) {
+        // Check that config mode is appropriate for the display snapshot.
+        for (const auto& existing_mode : snapshot->get()->modes()) {
+          if (AreModesEqual(*existing_mode.get(), *config.mode.value().get())) {
+            configure_success = true;
+            break;
+          }
         }
       }
+    } else {
+      // This is a request to turn off the display.
+      configure_success = true;
     }
-  } else {
-    // This is a request to turn off the display.
-    configure_success = true;
+    statuses.insert(std::make_pair(config.id, configure_success));
   }
 
-  configure_callbacks_.push(
-      base::BindOnce(std::move(callback), configure_success));
+  configure_callbacks_.push(base::BindOnce(std::move(callback), statuses));
 
   // Start the timer if it's not already running. If there are multiple queued
   // configuration requests then ConfigureDone() will handle starting the
diff --git a/ui/display/fake/fake_display_delegate.h b/ui/display/fake/fake_display_delegate.h
index 13f67e42..7e9c71c 100644
--- a/ui/display/fake/fake_display_delegate.h
+++ b/ui/display/fake/fake_display_delegate.h
@@ -84,7 +84,7 @@
   void RelinquishDisplayControl(DisplayControlCallback callback) override;
   void GetDisplays(GetDisplaysCallback callback) override;
   void Configure(
-      const display::DisplayConfigurationParams& display_config_params,
+      const std::vector<display::DisplayConfigurationParams>& config_requests,
       ConfigureCallback callback) override;
   void GetHDCPState(const DisplaySnapshot& output,
                     GetHDCPStateCallback callback) override;
diff --git a/ui/display/manager/configure_displays_task.cc b/ui/display/manager/configure_displays_task.cc
index 52e232e8..2af79d1c 100644
--- a/ui/display/manager/configure_displays_task.cc
+++ b/ui/display/manager/configure_displays_task.cc
@@ -73,6 +73,15 @@
   return width_idx * base::size(kDisplayResolutionSamples) + height_idx + 1;
 }
 
+std::__wrap_iter<const DisplayConfigureRequest*> GetRequestForDisplayId(
+    int64_t display_id,
+    const std::vector<DisplayConfigureRequest>& requests) {
+  return find_if(requests.begin(), requests.end(),
+                 [display_id](const DisplayConfigureRequest& request) {
+                   return request.display->display_id() == display_id;
+                 });
+}
+
 }  // namespace
 
 DisplayConfigureRequest::DisplayConfigureRequest(DisplaySnapshot* display,
@@ -110,32 +119,40 @@
   {
     base::AutoReset<bool> recursivity_guard(&is_configuring_, true);
     while (!pending_request_indexes_.empty()) {
-      size_t index = pending_request_indexes_.front();
-      DisplayConfigureRequest* request = &requests_[index];
-      pending_request_indexes_.pop();
-      const bool internal =
-          request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
-      base::UmaHistogramExactLinear(
-          internal ? "ConfigureDisplays.Internal.Modeset.Resolution"
-                   : "ConfigureDisplays.External.Modeset.Resolution",
-          ComputeDisplayResolutionEnum(request->mode),
-          base::size(kDisplayResolutionSamples) *
-                  base::size(kDisplayResolutionSamples) +
-              2);
+      // Loop over all the current requests, then it will loop again making sure
+      // no new requests were added and are pending.
+      std::vector<display::DisplayConfigurationParams> config_requests;
+      for (size_t i = 0; i < pending_request_indexes_.size(); ++i) {
+        size_t index = pending_request_indexes_.front();
+        DisplayConfigureRequest* request = &requests_[index];
+        pending_request_indexes_.pop();
 
-      base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
-          internal ? "ConfigureDisplays.Internal.Modeset.RefreshRate"
-                   : "ConfigureDisplays.External.Modeset.RefreshRate",
-          1, 240, 18, base::HistogramBase::kUmaTargetedHistogramFlag);
-      histogram->Add(request->mode ? std::round(request->mode->refresh_rate())
-                                   : 0);
+        const bool internal =
+            request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
+        base::UmaHistogramExactLinear(
+            internal ? "ConfigureDisplays.Internal.Modeset.Resolution"
+                     : "ConfigureDisplays.External.Modeset.Resolution",
+            ComputeDisplayResolutionEnum(request->mode),
+            base::size(kDisplayResolutionSamples) *
+                    base::size(kDisplayResolutionSamples) +
+                2);
+        base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
+            internal ? "ConfigureDisplays.Internal.Modeset.RefreshRate"
+                     : "ConfigureDisplays.External.Modeset.RefreshRate",
+            1, 240, 18, base::HistogramBase::kUmaTargetedHistogramFlag);
+        histogram->Add(request->mode ? std::round(request->mode->refresh_rate())
+                                     : 0);
 
-      display::DisplayConfigurationParams display_config_params(
-          request->display->display_id(), request->origin, request->mode);
-      delegate_->Configure(
-          display_config_params,
-          base::BindOnce(&ConfigureDisplaysTask::OnConfigured,
-                         weak_ptr_factory_.GetWeakPtr(), index));
+        display::DisplayConfigurationParams display_config_params(
+            request->display->display_id(), request->origin, request->mode);
+        config_requests.push_back(std::move(display_config_params));
+      }
+      if (!config_requests.empty()) {
+        delegate_->Configure(
+            config_requests,
+            base::BindOnce(&ConfigureDisplaysTask::OnConfigured,
+                           weak_ptr_factory_.GetWeakPtr()));
+      }
     }
   }
 
@@ -156,44 +173,83 @@
   Run();
 }
 
-void ConfigureDisplaysTask::OnConfigured(size_t index, bool success) {
-  DisplayConfigureRequest* request = &requests_[index];
-  VLOG(2) << "Configured status=" << success
-          << " display=" << request->display->display_id()
-          << " origin=" << request->origin.ToString()
-          << " mode=" << (request->mode ? request->mode->ToString() : "null");
+void ConfigureDisplaysTask::OnConfigured(
+    const base::flat_map<int64_t, bool>& statuses) {
+  bool config_success = true;
 
-  const bool internal =
-      request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
-  base::UmaHistogramBoolean(
-      internal ? "ConfigureDisplays.Internal.Modeset.AttemptSucceeded"
-               : "ConfigureDisplays.External.Modeset.AttemptSucceeded",
-      success);
+  // Check if all displays are successfully configured.
+  for (const auto& status : statuses) {
+    int64_t display_id = status.first;
+    bool display_success = status.second;
+    config_success &= display_success;
 
-  if (!success) {
-    request->mode = FindNextMode(*request->display, request->mode);
-    if (request->mode) {
-      pending_request_indexes_.push(index);
+    auto request = GetRequestForDisplayId(display_id, requests_);
+    DCHECK(request != requests_.end());
+
+    VLOG(2) << "Configured status=" << display_success
+            << " display=" << request->display->display_id()
+            << " origin=" << request->origin.ToString()
+            << " mode=" << (request->mode ? request->mode->ToString() : "null");
+
+    bool internal =
+        request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
+    base::UmaHistogramBoolean(
+        internal ? "ConfigureDisplays.Internal.Modeset.AttemptSucceeded"
+                 : "ConfigureDisplays.External.Modeset.AttemptSucceeded",
+        display_success);
+  }
+
+  if (config_success) {
+    for (const auto& status : statuses) {
+      auto request = GetRequestForDisplayId(status.first, requests_);
+      request->display->set_current_mode(request->mode);
+      request->display->set_origin(request->origin);
+    }
+  } else {
+    bool should_reconfigure = false;
+    // For the failing config, check if there is another mode to be requested.
+    // If there is one, attempt to reconfigure everything again.
+    for (const auto& status : statuses) {
+      int64_t display_id = status.first;
+      bool display_success = status.second;
+      if (!display_success) {
+        const DisplayConfigureRequest* request =
+            GetRequestForDisplayId(display_id, requests_).base();
+        const_cast<DisplayConfigureRequest*>(request)->mode =
+            FindNextMode(*request->display, request->mode);
+        should_reconfigure = !!request->mode;
+      }
+    }
+    // When reconfiguring, reconfigure all displays, not only the failing ones
+    // as they could potentially depend on each other.
+    if (should_reconfigure) {
+      for (const auto& status : statuses) {
+        auto const_iterator = GetRequestForDisplayId(status.first, requests_);
+        auto request = requests_.erase(const_iterator, const_iterator);
+        size_t index = std::distance(requests_.begin(), request);
+        pending_request_indexes_.push(index);
+      }
       if (task_status_ == SUCCESS)
         task_status_ = PARTIAL_SUCCESS;
-
       Run();
       return;
     }
-  } else {
-    request->display->set_current_mode(request->mode);
-    request->display->set_origin(request->origin);
   }
 
-  num_displays_configured_++;
+  // If no reconfigurations are happening, update the final state.
+  for (const auto& status : statuses) {
+    auto request = GetRequestForDisplayId(status.first, requests_);
+    bool internal =
+        request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
+    base::UmaHistogramBoolean(
+        internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus"
+                 : "ConfigureDisplays.External.Modeset.FinalStatus",
+        config_success);
+  }
 
-  base::UmaHistogramBoolean(
-      internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus"
-               : "ConfigureDisplays.External.Modeset.FinalStatus",
-      success);
-  if (!success)
+  num_displays_configured_ += statuses.size();
+  if (!config_success)
     task_status_ = ERROR;
-
   Run();
 }
 
diff --git a/ui/display/manager/configure_displays_task.h b/ui/display/manager/configure_displays_task.h
index 86368b3e..25ca3f7b 100644
--- a/ui/display/manager/configure_displays_task.h
+++ b/ui/display/manager/configure_displays_task.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "base/containers/queue.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -65,7 +66,7 @@
   void OnDisplaySnapshotsInvalidated() override;
 
  private:
-  void OnConfigured(size_t index, bool success);
+  void OnConfigured(const base::flat_map<int64_t, bool>& statuses);
 
   NativeDisplayDelegate* delegate_;  // Not owned.
 
diff --git a/ui/display/manager/test/test_native_display_delegate.cc b/ui/display/manager/test/test_native_display_delegate.cc
index 13323a00f..47b0f7f 100644
--- a/ui/display/manager/test/test_native_display_delegate.cc
+++ b/ui/display/manager/test/test_native_display_delegate.cc
@@ -70,14 +70,17 @@
 }
 
 void TestNativeDisplayDelegate::Configure(
-    const display::DisplayConfigurationParams& display_config_params,
+    const std::vector<display::DisplayConfigurationParams>& config_requests,
     ConfigureCallback callback) {
-  bool result = Configure(display_config_params);
+  base::flat_map<int64_t, bool> statuses;
+  for (const auto& config : config_requests)
+    statuses.insert(std::make_pair(config.id, Configure(config)));
+
   if (run_async_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), result));
+        FROM_HERE, base::BindOnce(std::move(callback), statuses));
   } else {
-    std::move(callback).Run(result);
+    std::move(callback).Run(statuses);
   }
 }
 
diff --git a/ui/display/manager/test/test_native_display_delegate.h b/ui/display/manager/test/test_native_display_delegate.h
index 8c94bc8..1bf5d89 100644
--- a/ui/display/manager/test/test_native_display_delegate.h
+++ b/ui/display/manager/test/test_native_display_delegate.h
@@ -58,7 +58,7 @@
   void RelinquishDisplayControl(DisplayControlCallback callback) override;
   void GetDisplays(GetDisplaysCallback callback) override;
   void Configure(
-      const display::DisplayConfigurationParams& display_config_params,
+      const std::vector<display::DisplayConfigurationParams>& config_requests,
       ConfigureCallback callback) override;
   void GetHDCPState(const DisplaySnapshot& output,
                     GetHDCPStateCallback callback) override;
diff --git a/ui/display/types/display_configuration_params.cc b/ui/display/types/display_configuration_params.cc
index b6101d7..60d0aa3 100644
--- a/ui/display/types/display_configuration_params.cc
+++ b/ui/display/types/display_configuration_params.cc
@@ -8,6 +8,20 @@
 
 DisplayConfigurationParams::DisplayConfigurationParams() = default;
 DisplayConfigurationParams::DisplayConfigurationParams(
+    DisplayConfigurationParams& other)
+    : id(other.id), origin(other.origin) {
+  if (other.mode)
+    mode = other.mode->get()->Clone();
+}
+
+DisplayConfigurationParams::DisplayConfigurationParams(
+    DisplayConfigurationParams&& other)
+    : id(other.id), origin(other.origin) {
+  if (other.mode)
+    mode = other.mode->get()->Clone();
+}
+
+DisplayConfigurationParams::DisplayConfigurationParams(
     int64_t id,
     const gfx::Point& origin,
     const display::DisplayMode* pmode)
diff --git a/ui/display/types/display_configuration_params.h b/ui/display/types/display_configuration_params.h
index d9739e0..31c44f62 100644
--- a/ui/display/types/display_configuration_params.h
+++ b/ui/display/types/display_configuration_params.h
@@ -16,6 +16,8 @@
 
 struct DISPLAY_TYPES_EXPORT DisplayConfigurationParams {
   DisplayConfigurationParams();
+  DisplayConfigurationParams(DisplayConfigurationParams& other);
+  DisplayConfigurationParams(DisplayConfigurationParams&& other);
   DisplayConfigurationParams(int64_t id,
                              const gfx::Point& origin,
                              const display::DisplayMode* pmode);
diff --git a/ui/display/types/native_display_delegate.h b/ui/display/types/native_display_delegate.h
index 60addf5..cb8980a 100644
--- a/ui/display/types/native_display_delegate.h
+++ b/ui/display/types/native_display_delegate.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "ui/display/types/display_configuration_params.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/display/types/display_types_export.h"
@@ -24,7 +25,8 @@
 
 using GetDisplaysCallback =
     base::OnceCallback<void(const std::vector<DisplaySnapshot*>&)>;
-using ConfigureCallback = base::OnceCallback<void(bool)>;
+using ConfigureCallback =
+    base::OnceCallback<void(const base::flat_map<int64_t, bool>&)>;
 using GetHDCPStateCallback = base::OnceCallback<void(bool, HDCPState)>;
 using SetHDCPStateCallback = base::OnceCallback<void(bool)>;
 using DisplayControlCallback = base::OnceCallback<void(bool)>;
@@ -56,7 +58,7 @@
   // represents disabling the display. The callback will return the status of
   // the operation.
   virtual void Configure(
-      const display::DisplayConfigurationParams& display_config_params,
+      const std::vector<display::DisplayConfigurationParams>& config_requests,
       ConfigureCallback callback) = 0;
 
   // Gets HDCP state of output.
diff --git a/ui/file_manager/file_manager/foreground/js/ui/multi_menu.js b/ui/file_manager/file_manager/foreground/js/ui/multi_menu.js
index 95e8e41..2352b28 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/multi_menu.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/multi_menu.js
@@ -205,14 +205,20 @@
      * @param {cr.ui.Menu} subMenu The child (sub) menu to be positioned.
      */
     positionSubMenu_(item, subMenu) {
+      const style = subMenu.style;
+
+      if (util.isFilesNg()) {
+        style.marginTop = '0';  // crbug.com/1066727
+      }
+
       // The sub-menu needs to sit aligned to the top and side of
       // the menu-item passed in. It also needs to fit inside the viewport
       const itemRect = item.getBoundingClientRect();
       const viewportWidth = window.innerWidth;
       const viewportHeight = window.innerHeight;
       const childRect = subMenu.getBoundingClientRect();
-      const style = subMenu.style;
       const maxShift = itemRect.width / 2;
+
       // See if it fits on the right, if not position on the left
       // if there's more room on the left.
       style.left = style.right = style.top = style.bottom = 'auto';
diff --git a/ui/gfx/x/x11.h b/ui/gfx/x/x11.h
index 2cae7c5..90d85e4 100644
--- a/ui/gfx/x/x11.h
+++ b/ui/gfx/x/x11.h
@@ -19,7 +19,6 @@
 #include <X11/X.h>
 #include <X11/XKBlib.h>
 #include <X11/Xatom.h>
-#include <X11/Xcursor/Xcursor.h>
 #include <X11/Xlib-xcb.h>
 #include <X11/Xregion.h>
 #include <X11/Xutil.h>
diff --git a/ui/gfx/x/x11_types.h b/ui/gfx/x/x11_types.h
index 0cc24233..9bede0e7 100644
--- a/ui/gfx/x/x11_types.h
+++ b/ui/gfx/x/x11_types.h
@@ -13,7 +13,6 @@
 #include "ui/gfx/x/connection.h"
 
 typedef unsigned long VisualID;
-typedef struct _XcursorImage XcursorImage;
 typedef union _XEvent XEvent;
 typedef struct _XImage XImage;
 typedef struct _XGC* GC;
diff --git a/ui/gfx/x/xproto_types.h b/ui/gfx/x/xproto_types.h
index de22342..9e8e04d 100644
--- a/ui/gfx/x/xproto_types.h
+++ b/ui/gfx/x/xproto_types.h
@@ -5,7 +5,6 @@
 #ifndef UI_GFX_X_XPROTO_TYPES_H_
 #define UI_GFX_X_XPROTO_TYPES_H_
 
-#include <X11/Xlib-xcb.h>
 #include <xcb/xcb.h>
 #include <xcb/xcbext.h>
 
diff --git a/ui/gl/gl_surface_glx.cc b/ui/gl/gl_surface_glx.cc
index cbb75138..f78b447 100644
--- a/ui/gl/gl_surface_glx.cc
+++ b/ui/gl/gl_surface_glx.cc
@@ -26,8 +26,13 @@
 #include "ui/base/x/x11_util.h"
 #include "ui/events/platform/platform_event_source.h"
 #include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/dri2.h"
+#include "ui/gfx/x/glx.h"
+#include "ui/gfx/x/present.h"
 #include "ui/gfx/x/x11.h"
 #include "ui/gfx/x/x11_types.h"
+#include "ui/gfx/x/xf86vidmode.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_implementation.h"
@@ -148,8 +153,8 @@
 
 class OMLSyncControlVSyncProvider : public SyncControlVSyncProvider {
  public:
-  explicit OMLSyncControlVSyncProvider(GLXWindow glx_window)
-      : SyncControlVSyncProvider(), glx_window_(glx_window) {}
+  explicit OMLSyncControlVSyncProvider(x11::Window window)
+      : SyncControlVSyncProvider(), window_(window) {}
 
   ~OMLSyncControlVSyncProvider() override = default;
 
@@ -157,29 +162,85 @@
   bool GetSyncValues(int64_t* system_time,
                      int64_t* media_stream_counter,
                      int64_t* swap_buffer_counter) override {
-    return glXGetSyncValuesOML(gfx::GetXDisplay(), glx_window_, system_time,
-                               media_stream_counter, swap_buffer_counter);
+    auto* connection = x11::Connection::Get();
+
+    // First try to get the counter values using the DRI2 extension.
+    if (auto reply = connection->dri2().GetMSC({window_}).Sync()) {
+      auto merge_counter = [](uint32_t hi, uint32_t lo) {
+        return (static_cast<uint64_t>(hi) << 32) | lo;
+      };
+      *system_time = merge_counter(reply->ust_hi, reply->ust_lo);
+      *media_stream_counter = merge_counter(reply->msc_hi, reply->msc_lo);
+      *swap_buffer_counter = merge_counter(reply->sbc_hi, reply->sbc_lo);
+      return true;
+    }
+
+    // Next try the present extension.
+    auto& present = connection->present();
+    // Check if the present extension is available.
+    if (!present.present())
+      return false;
+
+    // Issue a NotifyMSC request and listen for the resulting event which will
+    // contain the counter values.
+    auto context = connection->GenerateId<x11::Present::Event>();
+    present.SelectInput(
+        {context, window_, x11::Present::EventMask::CompleteNotify});
+    connection->present().NotifyMSC({window_});
+    present.SelectInput({context, window_, x11::Present::EventMask::NoEvent});
+    connection->Sync();
+    connection->ReadResponses();
+    for (const auto& event : connection->events()) {
+      auto* complete = event.As<x11::Present::CompleteNotifyEvent>();
+      if (complete && complete->kind == x11::Present::CompleteKind::NotifyMSC &&
+          complete->window == window_ && complete->serial == 0) {
+        *system_time = complete->ust;
+        *media_stream_counter = complete->msc;
+        *swap_buffer_counter = 0;
+        return true;
+      }
+    }
+
+    return false;
   }
 
   bool GetMscRate(int32_t* numerator, int32_t* denominator) override {
     if (!g_glx_get_msc_rate_oml_supported)
       return false;
 
-    if (!glXGetMscRateOML(gfx::GetXDisplay(), glx_window_, numerator,
-                          denominator)) {
-      // Once glXGetMscRateOML has been found to fail, don't try again,
+    auto* connection = x11::Connection::Get();
+    connection->xf86vidmode().SetClientVersion(
+        {x11::XF86VidMode::major_version, x11::XF86VidMode::minor_version});
+    auto reply = connection->xf86vidmode()
+                     .GetModeLine({connection->DefaultScreenId()})
+                     .Sync();
+    if (!reply) {
+      // Once GetModeLine has been found to fail, don't try again,
       // since each failing call may spew an error message.
       g_glx_get_msc_rate_oml_supported = false;
       return false;
     }
 
+    *numerator = static_cast<uint32_t>(reply->dotclock) * 1000;
+    *denominator = static_cast<uint32_t>(reply->vtotal) * reply->htotal;
+
+    // These adjustments are from mesa's __glxGetMscRate().
+    if (static_cast<bool>(reply->flags &
+                          x11::XF86VidMode::ModeFlag::Interlace)) {
+      *numerator *= 2;
+    }
+    if (static_cast<bool>(reply->flags &
+                          x11::XF86VidMode::ModeFlag::Composite_Sync)) {
+      *denominator *= 2;
+    }
+
     return true;
   }
 
   bool IsHWClock() const override { return true; }
 
  private:
-  GLXWindow glx_window_;
+  x11::Window window_;
 
   DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
 };
@@ -671,8 +732,8 @@
   }
 
   if (g_glx_oml_sync_control_supported) {
-    vsync_provider_ =
-        std::make_unique<OMLSyncControlVSyncProvider>(glx_window_);
+    vsync_provider_ = std::make_unique<OMLSyncControlVSyncProvider>(
+        static_cast<x11::Window>(window_));
     presentation_helper_ =
         std::make_unique<GLSurfacePresentationHelper>(vsync_provider_.get());
   } else if (g_glx_sgi_video_sync_supported) {
diff --git a/ui/ozone/demo/window_manager.cc b/ui/ozone/demo/window_manager.cc
index 473c383..611d6bff 100644
--- a/ui/ozone/demo/window_manager.cc
+++ b/ui/ozone/demo/window_manager.cc
@@ -89,10 +89,12 @@
 
     display::DisplayConfigurationParams display_config_params(
         display->display_id(), origin, display->native_mode());
+    std::vector<display::DisplayConfigurationParams> config_request;
+    config_request.push_back(std::move(display_config_params));
     delegate_->Configure(
-        display_config_params,
+        config_request,
         base::BindOnce(&WindowManager::OnDisplayConfigured,
-                       base::Unretained(this),
+                       base::Unretained(this), display->display_id(),
                        gfx::Rect(origin, display->native_mode()->size())));
     origin.Offset(display->native_mode()->size().width(), 0);
   }
@@ -106,8 +108,13 @@
   }
 }
 
-void WindowManager::OnDisplayConfigured(const gfx::Rect& bounds, bool success) {
-  if (success) {
+void WindowManager::OnDisplayConfigured(
+    const int64_t display_id,
+    const gfx::Rect& bounds,
+    const base::flat_map<int64_t, bool>& statuses) {
+  DCHECK_EQ(statuses.size(), 1UL);
+
+  if (statuses.at(display_id)) {
     std::unique_ptr<DemoWindow> window(
         new DemoWindow(this, renderer_factory_.get(), bounds));
     window->Start();
diff --git a/ui/ozone/demo/window_manager.h b/ui/ozone/demo/window_manager.h
index 14f3e4c84..0415b01 100644
--- a/ui/ozone/demo/window_manager.h
+++ b/ui/ozone/demo/window_manager.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "ui/display/types/native_display_observer.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/ozone/demo/renderer_factory.h"
@@ -38,7 +39,9 @@
  private:
   void OnDisplaysAcquired(
       const std::vector<display::DisplaySnapshot*>& displays);
-  void OnDisplayConfigured(const gfx::Rect& bounds, bool success);
+  void OnDisplayConfigured(const int64_t display_id,
+                           const gfx::Rect& bounds,
+                           const base::flat_map<int64_t, bool>& statuses);
 
   // display::NativeDisplayDelegate:
   void OnConfigurationChanged() override;
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.cc b/ui/ozone/platform/drm/gpu/drm_thread.cc
index d785219..789edc9 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread.cc
@@ -325,19 +325,23 @@
   std::move(callback).Run(display_manager_->GetDisplays());
 }
 
-void DrmThread::ConfigureNativeDisplay(
-    const display::DisplayConfigurationParams& display_config_params,
-    base::OnceCallback<void(bool)> callback) {
-  TRACE_EVENT0("drm", "DrmThread::ConfigureNativeDisplay");
+void DrmThread::ConfigureNativeDisplays(
+    const std::vector<display::DisplayConfigurationParams>& config_requests,
+    base::OnceCallback<void(const base::flat_map<int64_t, bool>&)> callback) {
+  TRACE_EVENT0("drm", "DrmThread::ConfigureNativeDisplays");
 
-  if (display_config_params.mode) {
-    std::move(callback).Run(display_manager_->ConfigureDisplay(
-        display_config_params.id, *display_config_params.mode.value(),
-        display_config_params.origin));
-  } else {
-    std::move(callback).Run(
-        display_manager_->DisableDisplay(display_config_params.id));
+  base::flat_map<int64_t, bool> statuses;
+  for (const auto& config : config_requests) {
+    bool status = false;
+    if (config.mode) {
+      status = display_manager_->ConfigureDisplay(
+          config.id, *config.mode.value(), config.origin);
+    } else {
+      status = display_manager_->DisableDisplay(config.id);
+    }
+    statuses.insert(std::make_pair(config.id, status));
   }
+  std::move(callback).Run(statuses);
 }
 
 void DrmThread::TakeDisplayControl(base::OnceCallback<void(bool)> callback) {
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.h b/ui/ozone/platform/drm/gpu/drm_thread.h
index e7d92c9..a744f482 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.h
+++ b/ui/ozone/platform/drm/gpu/drm_thread.h
@@ -147,9 +147,9 @@
       base::OnceCallback<void(MovableDisplaySnapshots)> callback) override;
   void AddGraphicsDevice(const base::FilePath& path, base::File file) override;
   void RemoveGraphicsDevice(const base::FilePath& path) override;
-  void ConfigureNativeDisplay(
-      const display::DisplayConfigurationParams& display_config_params,
-      base::OnceCallback<void(bool)> callback) override;
+  void ConfigureNativeDisplays(
+      const std::vector<display::DisplayConfigurationParams>& config_requests,
+      ConfigureNativeDisplaysCallback callback) override;
   void GetHDCPState(int64_t display_id,
                     base::OnceCallback<void(int64_t, bool, display::HDCPState)>
                         callback) override;
diff --git a/ui/ozone/platform/drm/host/drm_display_host.cc b/ui/ozone/platform/drm/host/drm_display_host.cc
index 5fe107f..23c44ce 100644
--- a/ui/ozone/platform/drm/host/drm_display_host.cc
+++ b/ui/ozone/platform/drm/host/drm_display_host.cc
@@ -35,18 +35,6 @@
   snapshot_ = std::move(params);
 }
 
-void DrmDisplayHost::Configure(
-    const display::DisplayConfigurationParams& display_config_params,
-    display::ConfigureCallback callback) {
-  if (is_dummy_) {
-    std::move(callback).Run(true);
-    return;
-  }
-
-  sender_->GpuConfigureNativeDisplay(display_config_params,
-                                     std::move(callback));
-}
-
 void DrmDisplayHost::GetHDCPState(display::GetHDCPStateCallback callback) {
   get_hdcp_callback_ = std::move(callback);
   if (!sender_->GpuGetHDCPState(snapshot_->display_id()))
diff --git a/ui/ozone/platform/drm/host/drm_display_host.h b/ui/ozone/platform/drm/host/drm_display_host.h
index a813db47..5bb6a11 100644
--- a/ui/ozone/platform/drm/host/drm_display_host.h
+++ b/ui/ozone/platform/drm/host/drm_display_host.h
@@ -29,11 +29,9 @@
   ~DrmDisplayHost() override;
 
   display::DisplaySnapshot* snapshot() const { return snapshot_.get(); }
+  bool is_dummy() const { return is_dummy_; }
 
   void UpdateDisplaySnapshot(std::unique_ptr<display::DisplaySnapshot> params);
-  void Configure(
-      const display::DisplayConfigurationParams& display_config_params,
-      display::ConfigureCallback callback);
   void GetHDCPState(display::GetHDCPStateCallback callback);
   void SetHDCPState(display::HDCPState state,
                     display::SetHDCPStateCallback callback);
diff --git a/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
index 44653604..e3429cb 100644
--- a/ui/ozone/platform/drm/host/drm_display_host_manager.cc
+++ b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
@@ -234,6 +234,23 @@
   }
 }
 
+void DrmDisplayHostManager::ConfigureDisplays(
+    const std::vector<display::DisplayConfigurationParams>& config_requests,
+    display::ConfigureCallback callback) {
+  base::flat_map<int64_t, bool> dummy_statuses;
+  bool is_any_dummy = false;
+  for (auto& config : config_requests) {
+    is_any_dummy |= GetDisplay(config.id)->is_dummy();
+    dummy_statuses.insert(std::make_pair(config.id, true));
+  }
+  if (is_any_dummy) {
+    std::move(callback).Run(dummy_statuses);
+    return;
+  }
+
+  proxy_->GpuConfigureNativeDisplays(config_requests, std::move(callback));
+}
+
 void DrmDisplayHostManager::OnDeviceEvent(const DeviceEvent& event) {
   if (event.device_type() != DeviceEvent::DISPLAY)
     return;
diff --git a/ui/ozone/platform/drm/host/drm_display_host_manager.h b/ui/ozone/platform/drm/host/drm_display_host_manager.h
index 9a3606e..bddc9b5 100644
--- a/ui/ozone/platform/drm/host/drm_display_host_manager.h
+++ b/ui/ozone/platform/drm/host/drm_display_host_manager.h
@@ -51,6 +51,9 @@
   void TakeDisplayControl(display::DisplayControlCallback callback);
   void RelinquishDisplayControl(display::DisplayControlCallback callback);
   void UpdateDisplays(display::GetDisplaysCallback callback);
+  void ConfigureDisplays(
+      const std::vector<display::DisplayConfigurationParams>& config_requests,
+      display::ConfigureCallback callback);
 
   // DeviceEventObserver overrides:
   void OnDeviceEvent(const DeviceEvent& event) override;
diff --git a/ui/ozone/platform/drm/host/drm_native_display_delegate.cc b/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
index 7419285..c239f99e 100644
--- a/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
+++ b/ui/ozone/platform/drm/host/drm_native_display_delegate.cc
@@ -51,11 +51,9 @@
 }
 
 void DrmNativeDisplayDelegate::Configure(
-    const display::DisplayConfigurationParams& display_config_params,
+    const std::vector<display::DisplayConfigurationParams>& config_requests,
     display::ConfigureCallback callback) {
-  DrmDisplayHost* display =
-      display_manager_->GetDisplay(display_config_params.id);
-  display->Configure(display_config_params, std::move(callback));
+  display_manager_->ConfigureDisplays(config_requests, std::move(callback));
 }
 
 void DrmNativeDisplayDelegate::GetHDCPState(
diff --git a/ui/ozone/platform/drm/host/drm_native_display_delegate.h b/ui/ozone/platform/drm/host/drm_native_display_delegate.h
index 0cd2546..fd141f1 100644
--- a/ui/ozone/platform/drm/host/drm_native_display_delegate.h
+++ b/ui/ozone/platform/drm/host/drm_native_display_delegate.h
@@ -30,7 +30,7 @@
       display::DisplayControlCallback callback) override;
   void GetDisplays(display::GetDisplaysCallback callback) override;
   void Configure(
-      const display::DisplayConfigurationParams& display_config_params,
+      const std::vector<display::DisplayConfigurationParams>& config_requests,
       display::ConfigureCallback callback) override;
   void GetHDCPState(const display::DisplaySnapshot& output,
                     display::GetHDCPStateCallback callback) override;
diff --git a/ui/ozone/platform/drm/host/gpu_thread_adapter.h b/ui/ozone/platform/drm/host/gpu_thread_adapter.h
index 535d59c..c782923 100644
--- a/ui/ozone/platform/drm/host/gpu_thread_adapter.h
+++ b/ui/ozone/platform/drm/host/gpu_thread_adapter.h
@@ -44,8 +44,8 @@
   virtual bool GpuRemoveGraphicsDevice(const base::FilePath& path) = 0;
 
   // Services needed by DrmDisplayHost
-  virtual void GpuConfigureNativeDisplay(
-      const display::DisplayConfigurationParams& display_config_params,
+  virtual void GpuConfigureNativeDisplays(
+      const std::vector<display::DisplayConfigurationParams>& config_requests,
       display::ConfigureCallback callback) = 0;
   virtual bool GpuGetHDCPState(int64_t display_id) = 0;
   virtual bool GpuSetHDCPState(int64_t display_id,
diff --git a/ui/ozone/platform/drm/host/host_drm_device.cc b/ui/ozone/platform/drm/host/host_drm_device.cc
index 909d84a7..960df0d 100644
--- a/ui/ozone/platform/drm/host/host_drm_device.cc
+++ b/ui/ozone/platform/drm/host/host_drm_device.cc
@@ -128,15 +128,18 @@
   return true;
 }
 
-void HostDrmDevice::GpuConfigureNativeDisplay(
-    const display::DisplayConfigurationParams& display_config_params,
+void HostDrmDevice::GpuConfigureNativeDisplays(
+    const std::vector<display::DisplayConfigurationParams>& config_requests,
     display::ConfigureCallback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(on_ui_thread_);
   if (IsConnected()) {
-    drm_device_->ConfigureNativeDisplay(display_config_params,
-                                        std::move(callback));
+    drm_device_->ConfigureNativeDisplays(config_requests, std::move(callback));
   } else {
-    std::move(callback).Run(false);
+    // If not connected, report failure to config.
+    base::flat_map<int64_t, bool> dummy_statuses;
+    for (const auto& config : config_requests)
+      dummy_statuses.insert(std::make_pair(config.id, false));
+    std::move(callback).Run(dummy_statuses);
   }
 }
 
diff --git a/ui/ozone/platform/drm/host/host_drm_device.h b/ui/ozone/platform/drm/host/host_drm_device.h
index 11b90c6..2484d706 100644
--- a/ui/ozone/platform/drm/host/host_drm_device.h
+++ b/ui/ozone/platform/drm/host/host_drm_device.h
@@ -65,8 +65,8 @@
   bool GpuRemoveGraphicsDevice(const base::FilePath& path) override;
 
   // Services needed by DrmDisplayHost
-  void GpuConfigureNativeDisplay(
-      const display::DisplayConfigurationParams& display_config_params,
+  void GpuConfigureNativeDisplays(
+      const std::vector<display::DisplayConfigurationParams>& config_requests,
       display::ConfigureCallback callback) override;
   bool GpuGetHDCPState(int64_t display_id) override;
   bool GpuSetHDCPState(int64_t display_id, display::HDCPState state) override;
diff --git a/ui/ozone/public/mojom/drm_device.mojom b/ui/ozone/public/mojom/drm_device.mojom
index 668b9a9..6504ea5d 100644
--- a/ui/ozone/public/mojom/drm_device.mojom
+++ b/ui/ozone/public/mojom/drm_device.mojom
@@ -51,10 +51,11 @@
   // Instructs the GPU to abandon a DRM device.
   RemoveGraphicsDevice(mojo_base.mojom.FilePath path);
 
-  // Configures (Enables/Disables) a DRM display, returning true on success.
-  ConfigureNativeDisplay(
-      display.mojom.DisplayConfigurationParams display_config_params) =>
-      (bool success);
+  // Configures (Enables/Disables) DRM displays, returns a map: each configured
+  // display ID to its status, true on success.
+  ConfigureNativeDisplays(
+      array<display.mojom.DisplayConfigurationParams> config_requests) =>
+    (map<int64, bool> statuses);
 
   // Gets or sets high-definition content protection (HDCP) (DRM as in
   // digital rights management) state.
diff --git a/ui/platform_window/x11/x11_window.cc b/ui/platform_window/x11/x11_window.cc
index 6575bcbf..a666bcc 100644
--- a/ui/platform_window/x11/x11_window.cc
+++ b/ui/platform_window/x11/x11_window.cc
@@ -399,7 +399,7 @@
 }
 
 void X11Window::SetCursor(PlatformCursor cursor) {
-  XWindow::SetCursor(static_cast<X11Cursor*>(cursor)->xcursor());
+  XWindow::SetCursor(static_cast<X11Cursor*>(cursor));
 }
 
 void X11Window::MoveCursorTo(const gfx::Point& location) {
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 57921db3..d4bfa5aa 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -3116,20 +3116,18 @@
 }
 
 void MenuController::SetHotTrackedButton(Button* hot_button) {
-  if (hot_button == hot_button_) {
-    // Hot-tracked state may change outside of the MenuController. Correct it.
-    if (hot_button && !hot_button->IsHotTracked()) {
-      hot_button->SetHotTracked(true);
-      hot_button->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
-    }
-    return;
-  }
-  if (hot_button_)
+  // If we're providing a new hot-tracked button, first remove the existing one.
+  if (hot_button_ && hot_button_ != hot_button) {
     hot_button_->SetHotTracked(false);
+    hot_button_->GetViewAccessibility().EndPopupFocusOverride();
+  }
+
+  // Then set the new one.
   hot_button_ = hot_button;
-  if (hot_button) {
-    hot_button->SetHotTracked(true);
-    hot_button->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
+  if (hot_button_ && !hot_button_->IsHotTracked()) {
+    hot_button_->GetViewAccessibility().SetPopupFocusOverride();
+    hot_button_->SetHotTracked(true);
+    hot_button_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
   }
 }
 
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 428df308..a000a81 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -12,6 +12,7 @@
 #include "base/command_line.h"
 #include "base/containers/adapters.h"
 #include "base/feature_list.h"
+#include "base/i18n/rtl.h"
 #include "base/macros.h"
 #include "base/notreached.h"
 #include "base/scoped_observer.h"
@@ -341,7 +342,7 @@
     // In RTL mode, if our width has changed, our children's mirrored bounds
     // will have changed. Update the child's layer bounds, or if it is not a
     // layer, the bounds of any layers inside the child.
-    if (base::i18n::IsRTL() && bounds_.width() != prev.width()) {
+    if (GetMirrored() && bounds_.width() != prev.width()) {
       for (View* child : children_) {
         child->UpdateChildLayerBounds(
             LayerOffsetData(layer()->device_scale_factor(),
@@ -586,7 +587,7 @@
   gfx::ScrollOffset scroll_offset = layer()->CurrentScrollOffset();
   // Offsets for layer-based scrolling are never negative, but the horizontal
   // scroll direction is reversed in RTL via canvas flipping.
-  transform.Translate((base::i18n::IsRTL() ? 1 : -1) * scroll_offset.x(),
+  transform.Translate((GetMirrored() ? 1 : -1) * scroll_offset.x(),
                       -scroll_offset.y());
   return transform;
 }
@@ -734,7 +735,7 @@
 }
 
 int View::GetMirroredXForRect(const gfx::Rect& rect) const {
-  return base::i18n::IsRTL() ? (width() - rect.x() - rect.width()) : rect.x();
+  return GetMirrored() ? (width() - rect.x() - rect.width()) : rect.x();
 }
 
 gfx::Rect View::GetMirroredRect(const gfx::Rect& rect) const {
@@ -744,11 +745,11 @@
 }
 
 int View::GetMirroredXInView(int x) const {
-  return base::i18n::IsRTL() ? width() - x : x;
+  return GetMirrored() ? width() - x : x;
 }
 
 int View::GetMirroredXWithWidthInView(int x, int w) const {
-  return base::i18n::IsRTL() ? width() - x - w : x;
+  return GetMirrored() ? width() - x - w : x;
 }
 
 // Layout ----------------------------------------------------------------------
@@ -1145,6 +1146,10 @@
   flip_canvas_on_paint_for_rtl_ui_ = enable;
 }
 
+bool View::GetMirrored() const {
+  return is_mirrored_.value_or(base::i18n::IsRTL());
+}
+
 // Input -----------------------------------------------------------------------
 
 View* View::GetEventHandlerForPoint(const gfx::Point& point) {
@@ -3006,6 +3011,7 @@
 ADD_PROPERTY_METADATA(View, int, ID)
 ADD_READONLY_PROPERTY_METADATA(View, gfx::Size, MaximumSize)
 ADD_READONLY_PROPERTY_METADATA(View, gfx::Size, MinimumSize)
+ADD_PROPERTY_METADATA(View, bool, Mirrored)
 ADD_PROPERTY_METADATA(View, bool, Visible)
 END_METADATA()
 
diff --git a/ui/views/view.h b/ui/views/view.h
index a7302ff..3c45851 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -23,6 +23,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/optional.h"
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "ui/accessibility/ax_enums.mojom-forward.h"
@@ -907,7 +908,7 @@
   // Enables or disables flipping of the gfx::Canvas during Paint(). Note that
   // if canvas flipping is enabled, the canvas will be flipped only if the UI
   // layout is right-to-left; that is, the canvas will be flipped only if
-  // base::i18n::IsRTL() returns true.
+  // GetMirrored() is true.
   //
   // Enabling canvas flipping is useful for leaf views that draw an image that
   // needs to be flipped horizontally when the UI layout is right-to-left
@@ -916,6 +917,19 @@
   // the UI directionality.
   virtual void EnableCanvasFlippingForRTLUI(bool enable);
 
+  // When set, this view will ignore base::l18n::IsRTL() and instead be drawn
+  // according to |is_mirrored|.
+  //
+  // This is useful for views that should be displayed the same regardless of UI
+  // direction. Unlike EnableCanvasFlippingForRTLUI this setting has an effect
+  // on the visual order of child views.
+  //
+  // This setting does not propagate to child views. So while the visual order
+  // of this view's children may change, the visual order of this view's
+  // grandchildren in relation to their parents are unchanged.
+  void SetMirrored(bool is_mirrored) { is_mirrored_ = is_mirrored; }
+  bool GetMirrored() const;
+
   // Input ---------------------------------------------------------------------
   // The points, rects, mouse locations, and touch locations in the following
   // functions are in the view's coordinates, except for a RootView.
@@ -1976,6 +1990,13 @@
   // right-to-left locales for this View.
   bool flip_canvas_on_paint_for_rtl_ui_ = false;
 
+  // Controls whether GetTransform(), the mirroring functions, and the like
+  // horizontally mirror. This controls how child views are physically
+  // positioned onscreen. The default behavior should be correct in most cases,
+  // but can be overridden if a particular view must always be laid out in some
+  // direction regardless of the application's default UI direction.
+  base::Optional<bool> is_mirrored_;
+
   // Accelerated painting ------------------------------------------------------
 
   // Whether layer painting was explicitly set by a call to |SetPaintToLayer()|.
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index 4117b104..fc05e41 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -51,6 +51,7 @@
 #include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/layout/box_layout.h"
 #include "ui/views/metadata/metadata_types.h"
 #include "ui/views/paint_info.h"
 #include "ui/views/test/view_metadata_test_utils.h"
@@ -3782,6 +3783,62 @@
   EXPECT_EQ(1, v1.GetIndexOf(&v3));
 }
 
+TEST_F(ViewTest, UseMirroredLayoutDisableMirroring) {
+  base::i18n::SetICUDefaultLocale("ar");
+  ASSERT_TRUE(base::i18n::IsRTL());
+
+  View parent, child1, child2;
+  parent.SetLayoutManager(
+      std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal));
+
+  child1.SetPreferredSize(gfx::Size(10, 10));
+  child2.SetPreferredSize(gfx::Size(10, 10));
+
+  parent.AddChildView(&child1);
+  parent.AddChildView(&child2);
+  parent.SizeToPreferredSize();
+
+  EXPECT_EQ(child1.GetNextFocusableView(), &child2);
+  EXPECT_GT(child1.GetMirroredX(), child2.GetMirroredX());
+  EXPECT_LT(child1.x(), child2.x());
+  EXPECT_NE(parent.GetMirroredXInView(5), 5);
+
+  parent.SetMirrored(false);
+
+  EXPECT_EQ(child1.GetNextFocusableView(), &child2);
+  EXPECT_GT(child2.GetMirroredX(), child1.GetMirroredX());
+  EXPECT_LT(child1.x(), child2.x());
+  EXPECT_EQ(parent.GetMirroredXInView(5), 5);
+}
+
+TEST_F(ViewTest, UseMirroredLayoutEnableMirroring) {
+  base::i18n::SetICUDefaultLocale("en");
+  ASSERT_FALSE(base::i18n::IsRTL());
+
+  View parent, child1, child2;
+  parent.SetLayoutManager(
+      std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal));
+
+  child1.SetPreferredSize(gfx::Size(10, 10));
+  child2.SetPreferredSize(gfx::Size(10, 10));
+
+  parent.AddChildView(&child1);
+  parent.AddChildView(&child2);
+  parent.SizeToPreferredSize();
+
+  EXPECT_EQ(child1.GetNextFocusableView(), &child2);
+  EXPECT_LT(child1.GetMirroredX(), child2.GetMirroredX());
+  EXPECT_LT(child1.x(), child2.x());
+  EXPECT_NE(parent.GetMirroredXInView(5), 15);
+
+  parent.SetMirrored(true);
+
+  EXPECT_EQ(child1.GetNextFocusableView(), &child2);
+  EXPECT_LT(child2.GetMirroredX(), child1.GetMirroredX());
+  EXPECT_LT(child1.x(), child2.x());
+  EXPECT_EQ(parent.GetMirroredXInView(5), 15);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // FocusManager
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc
index 80090c4..b9df6e0 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc
@@ -171,13 +171,11 @@
   auto* last_cursor = static_cast<ui::X11Cursor*>(
       source_window->GetHost()->last_cursor().platform());
   move_loop_->RunMoveLoop(
-      !source_window->HasCapture(),
-      last_cursor ? last_cursor->xcursor() : x11::None,
+      !source_window->HasCapture(), last_cursor,
       static_cast<ui::X11Cursor*>(
           cursor_manager_
               ->GetInitializedCursor(ui::mojom::CursorType::kGrabbing)
-              .platform())
-          ->xcursor());
+              .platform()));
 
   if (alive) {
     auto resulting_operation = negotiated_operation();
@@ -354,10 +352,8 @@
       cursor_type = ui::mojom::CursorType::kDndLink;
       break;
   }
-  move_loop_->UpdateCursor(
-      static_cast<ui::X11Cursor*>(
-          cursor_manager_->GetInitializedCursor(cursor_type).platform())
-          ->xcursor());
+  move_loop_->UpdateCursor(static_cast<ui::X11Cursor*>(
+      cursor_manager_->GetInitializedCursor(cursor_type).platform()));
 }
 
 void DesktopDragDropClientAuraX11::OnBeginForeignDrag(x11::Window window) {
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc
index 34352ba..3a79ed7 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc
@@ -83,9 +83,9 @@
 
   // ui::X11MoveLoop:
   bool RunMoveLoop(bool can_grab_pointer,
-                   ::Cursor old_cursor,
-                   ::Cursor new_cursor) override;
-  void UpdateCursor(::Cursor cursor) override;
+                   scoped_refptr<ui::X11Cursor> old_cursor,
+                   scoped_refptr<ui::X11Cursor> new_cursor) override;
+  void UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) override;
   void EndMoveLoop() override;
 
  private:
@@ -226,8 +226,8 @@
 }
 
 bool TestMoveLoop::RunMoveLoop(bool can_grab_pointer,
-                               ::Cursor old_cursor,
-                               ::Cursor new_cursor) {
+                               scoped_refptr<ui::X11Cursor> old_cursor,
+                               scoped_refptr<ui::X11Cursor> new_cursor) {
   is_running_ = true;
   base::RunLoop run_loop;
   quit_closure_ = run_loop.QuitClosure();
@@ -235,7 +235,7 @@
   return true;
 }
 
-void TestMoveLoop::UpdateCursor(::Cursor cursor) {}
+void TestMoveLoop::UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) {}
 
 void TestMoveLoop::EndMoveLoop() {
   if (is_running_) {
diff --git a/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc b/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc
index d861efd..7beb018d7 100644
--- a/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc
+++ b/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc
@@ -84,9 +84,9 @@
 
   // ui::X11MoveLoop:
   bool RunMoveLoop(bool can_grab_pointer,
-                   ::Cursor old_cursor,
-                   ::Cursor new_cursor) override;
-  void UpdateCursor(::Cursor cursor) override;
+                   scoped_refptr<ui::X11Cursor> old_cursor,
+                   scoped_refptr<ui::X11Cursor> new_cursor) override;
+  void UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) override;
   void EndMoveLoop() override;
 
  private:
@@ -260,8 +260,8 @@
 }
 
 bool TestMoveLoop::RunMoveLoop(bool can_grab_pointer,
-                               ::Cursor old_cursor,
-                               ::Cursor new_cursor) {
+                               scoped_refptr<ui::X11Cursor> old_cursor,
+                               scoped_refptr<ui::X11Cursor> new_cursor) {
   is_running_ = true;
   base::RunLoop run_loop;
   quit_closure_ = run_loop.QuitClosure();
@@ -269,7 +269,7 @@
   return true;
 }
 
-void TestMoveLoop::UpdateCursor(::Cursor cursor) {}
+void TestMoveLoop::UpdateCursor(scoped_refptr<ui::X11Cursor> cursor) {}
 
 void TestMoveLoop::EndMoveLoop() {
   if (is_running_) {
@@ -324,13 +324,11 @@
   auto* last_cursor = static_cast<ui::X11Cursor*>(
       source_window->GetHost()->last_cursor().platform());
   loop_->RunMoveLoop(
-      !source_window->HasCapture(),
-      last_cursor ? last_cursor->xcursor() : x11::None,
+      !source_window->HasCapture(), last_cursor,
       static_cast<ui::X11Cursor*>(
           cursor_manager_
               ->GetInitializedCursor(ui::mojom::CursorType::kGrabbing)
-              .platform())
-          ->xcursor());
+              .platform()));
 
   auto resulting_operation = negotiated_operation();
   CleanupDrag();
diff --git a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
index 294bfd1..a5102d5 100644
--- a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
+++ b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
@@ -64,7 +64,7 @@
         padding: 8px 0;
       }
     </style>
-    <dialog id="dialog" on-close="onNativeDialogClose_"
+    <dialog id="dialog" part="dialog" on-close="onNativeDialogClose_"
         role="application" aria-roledescription$="[[roleDescription]]">
       <div id="wrapper" class="item-wrapper" role="menu" tabindex="-1">
         <slot id="contentNode"></slot>
diff --git a/weblayer/browser/android/javatests/BUILD.gn b/weblayer/browser/android/javatests/BUILD.gn
index 20afb64..8ef610ae 100644
--- a/weblayer/browser/android/javatests/BUILD.gn
+++ b/weblayer/browser/android/javatests/BUILD.gn
@@ -49,6 +49,7 @@
     "//third_party/android_deps:android_support_v4_java",
     "//third_party/android_deps:androidx_core_core_java",
     "//third_party/android_deps:androidx_fragment_fragment_java",
+    "//third_party/android_deps:androidx_test_runner_java",
     "//third_party/android_deps:espresso_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
@@ -82,6 +83,7 @@
     "//content/public/test/android:content_java_test_support",
     "//net/android:net_java_test_support",
     "//third_party/android_deps:androidx_fragment_fragment_java",
+    "//third_party/android_deps:androidx_test_runner_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/hamcrest:hamcrest_java",
@@ -230,6 +232,7 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//content/public/test/android:content_java_test_support",
+    "//third_party/android_deps:androidx_test_runner_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/junit:junit",
diff --git a/weblayer/browser/android/javatests/skew/build_weblayer_instrumentation_test_cipd_pkg.py b/weblayer/browser/android/javatests/skew/build_weblayer_instrumentation_test_cipd_pkg.py
index 482ff90..0868a5d5 100755
--- a/weblayer/browser/android/javatests/skew/build_weblayer_instrumentation_test_cipd_pkg.py
+++ b/weblayer/browser/android/javatests/skew/build_weblayer_instrumentation_test_cipd_pkg.py
@@ -19,6 +19,7 @@
 import shutil
 import subprocess
 import sys
+import re
 import tempfile
 import zipfile
 
@@ -31,6 +32,8 @@
 MB_CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                               'mb_config.pyl')
 
+CHROMIUM_VERSION_REGEX = r'\d+\.\d+\.\d+\.\d+$'
+
 # CIPD package path.
 # https://chrome-infra-packages.appspot.com/p/chromium/testing/weblayer-x86/+/
 CIPD_PKG_PATH='chromium/testing/weblayer-x86'
@@ -92,6 +95,16 @@
   subprocess.check_call(cmd)
 
 
+def get_chromium_version():
+  with open(os.path.join(SRC_DIR, 'chrome', 'VERSION')) as f:
+    version = '.'.join(line[line.index('=') + 1:]
+                       for line in f.read().splitlines())
+  if not re.match(CHROMIUM_VERSION_REGEX, version):
+    raise ValueError("Chromium version, '%s', is not in proper format" %
+                     version)
+  return version
+
+
 def main():
   parser = argparse.ArgumentParser(
       description='Package weblayer instrumentation tests for CIPD.')
@@ -99,8 +112,9 @@
       '--cipd_out',
       required=True,
       help="Output filename for resulting .cipd file.")
-  args = parser.parse_args()
 
+  args = parser.parse_args()
+  chromium_version = get_chromium_version()
   with tempfile.TemporaryDirectory() as tmp_dir, \
        temporarily_chdir_to_src([args.cipd_out]) as cipd_out_src_rel_paths:
     # Create zip archive of test target.
@@ -118,8 +132,9 @@
     build_cipd_pkg(extracted, tmp_cipd_filename)
     shutil.move(tmp_cipd_filename, cipd_out_src_rel_paths[0])
 
-    print(('Use "cipd pkg-register %s -verbose -tag \'version:<branch>\'" ' +
-           'to upload package to the cipd server.') % args.cipd_out)
+    print(('Use "cipd pkg-register %s -verbose -tag \'version:%s\'" ' +
+           'to upload package to the cipd server.') %
+          (args.cipd_out, chromium_version))
     print('Use "cipd set-ref chromium/testing/weblayer-x86 --version ' +
           '<CIPD instance version> -ref m<milestone>" to update the ref.')
     print('The CIPD instance version can be found on the "Instance" line ' +
diff --git a/weblayer/browser/java/BUILD.gn b/weblayer/browser/java/BUILD.gn
index 7f6bfdb7..3b260dc 100644
--- a/weblayer/browser/java/BUILD.gn
+++ b/weblayer/browser/java/BUILD.gn
@@ -187,16 +187,19 @@
     "//services/network/public/mojom:cookies_mojom_java",
     "//services/network/public/mojom:mojom_java",
     "//services/service_manager/public/java:service_manager_java",
+    "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_appcompat_appcompat_java",
     "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//third_party/android_deps:androidx_core_core_java",
     "//third_party/android_deps:androidx_fragment_fragment_java",
+    "//third_party/android_deps:androidx_media_media_java",
     "//third_party/android_deps:androidx_preference_preference_java",
     "//third_party/android_deps:com_google_android_material_material_java",
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
     "//ui/android:ui_full_java",
     "//ui/android:ui_java",
     "//url:gurl_java",
+    "//url:origin_java",
   ]
   srcjar_deps = [
     ":generated_enums",
@@ -235,6 +238,7 @@
     ":interfaces_java",
     ":java",
     ":weblayer_test_resources",
+    "//base:base_java",
     "//base:jni_java",
     "//components/infobars/android:java",
     "//components/location/android:location_java",
@@ -243,6 +247,7 @@
     "//net/android:net_java",
     "//services/device/public/java:geolocation_java",
     "//services/device/public/java:geolocation_java_test_support",
+    "//third_party/android_deps:androidx_annotation_annotation_java",
     "//ui/android:ui_full_java",
   ]
   srcjar_deps = [ ":test_aidl" ]
diff --git a/weblayer/public/javatests/BUILD.gn b/weblayer/public/javatests/BUILD.gn
index db666ce4..5bb56de 100644
--- a/weblayer/public/javatests/BUILD.gn
+++ b/weblayer/public/javatests/BUILD.gn
@@ -14,6 +14,7 @@
   deps = [
     "//base:base_java",
     "//base:base_java_test_support",
+    "//third_party/android_deps:androidx_test_runner_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/junit:junit",
     "//weblayer/public/java",
diff --git a/weblayer/public/javatestutil/BUILD.gn b/weblayer/public/javatestutil/BUILD.gn
index 5fd626d..35e4982 100644
--- a/weblayer/public/javatestutil/BUILD.gn
+++ b/weblayer/public/javatestutil/BUILD.gn
@@ -11,6 +11,7 @@
 
   deps = [
     "//base:base_java_test_support",
+    "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/junit:junit",
     "//weblayer/browser/java:interfaces_java",
     "//weblayer/browser/java:test_java",